aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS4
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/cpu/armv8/fsl-layerscape/cpu.c2
-rw-r--r--arch/arm/cpu/armv8/fsl-layerscape/fdt.c2
-rw-r--r--arch/arm/mach-tegra/board2.c14
-rw-r--r--arch/sandbox/cpu/cpu.c153
-rw-r--r--arch/sandbox/cpu/os.c27
-rw-r--r--arch/sandbox/cpu/state.c8
-rw-r--r--arch/sandbox/include/asm/setjmp.h5
-rw-r--r--arch/sandbox/include/asm/state.h21
-rw-r--r--arch/x86/lib/e820.c4
-rw-r--r--cmd/bootefi.c187
-rw-r--r--cmd/fat.c34
-rw-r--r--common/bootm.c2
-rw-r--r--common/image-fdt.c2
-rw-r--r--configs/qemu_arm64_defconfig1
-rw-r--r--configs/qemu_arm_defconfig1
-rw-r--r--configs/sandbox_defconfig1
-rw-r--r--configs/vf610twr_defconfig1
-rw-r--r--configs/vf610twr_nand_defconfig1
-rw-r--r--drivers/rtc/pl031.c126
-rw-r--r--drivers/serial/serial_efi.c2
-rw-r--r--drivers/video/vidconsole-uclass.c8
-rw-r--r--fs/fat/fat.c67
-rw-r--r--fs/fat/fat_write.c1185
-rw-r--r--fs/fs.c87
-rw-r--r--include/capitalization.h2028
-rw-r--r--include/charset.h195
-rw-r--r--include/config_distro_bootcmd.h16
-rw-r--r--include/configs/qemu-arm.h3
-rw-r--r--include/cp1250.h40
-rw-r--r--include/cp437.h40
-rw-r--r--include/efi.h1
-rw-r--r--include/efi_api.h123
-rw-r--r--include/efi_loader.h59
-rw-r--r--include/efi_selftest.h18
-rw-r--r--include/fat.h4
-rw-r--r--include/fs.h22
-rw-r--r--include/os.h21
-rw-r--r--include/test/suites.h3
-rw-r--r--lib/Makefile5
-rw-r--r--lib/charset.c397
-rw-r--r--lib/efi_driver/efi_uclass.c86
-rw-r--r--lib/efi_loader/Kconfig12
-rw-r--r--lib/efi_loader/Makefile16
-rw-r--r--lib/efi_loader/efi_bootmgr.c2
-rw-r--r--lib/efi_loader/efi_boottime.c177
-rw-r--r--lib/efi_loader/efi_console.c650
-rw-r--r--lib/efi_loader/efi_device_path.c4
-rw-r--r--lib/efi_loader/efi_device_path_to_text.c10
-rw-r--r--lib/efi_loader/efi_file.c88
-rw-r--r--lib/efi_loader/efi_image_loader.c23
-rw-r--r--lib/efi_loader/efi_memory.c47
-rw-r--r--lib/efi_loader/efi_root_node.c79
-rw-r--r--lib/efi_loader/efi_runtime.c17
-rw-r--r--lib/efi_loader/efi_unicode_collation.c329
-rw-r--r--lib/efi_loader/efi_variable.c52
-rw-r--r--lib/efi_selftest/Kconfig2
-rw-r--r--lib/efi_selftest/Makefile3
-rw-r--r--lib/efi_selftest/efi_selftest_console.c2
-rw-r--r--lib/efi_selftest/efi_selftest_loaded_image.c108
-rw-r--r--lib/efi_selftest/efi_selftest_manageprotocols.c21
-rw-r--r--lib/efi_selftest/efi_selftest_textinput.c136
-rw-r--r--lib/efi_selftest/efi_selftest_textinputex.c198
-rw-r--r--lib/efi_selftest/efi_selftest_unicode_collation.c260
-rw-r--r--lib/efi_selftest/efi_selftest_util.c93
-rw-r--r--lib/vsprintf.c29
-rw-r--r--scripts/config_whitelist.txt1
-rw-r--r--test/Kconfig8
-rw-r--r--test/Makefile1
-rw-r--r--test/cmd_ut.c13
-rwxr-xr-xtest/fs/fs-test.sh24
-rw-r--r--test/print_ut.c6
-rw-r--r--test/py/tests/test_efi_selftest.py151
-rw-r--r--test/py/tests/test_fs/conftest.py392
-rw-r--r--test/py/tests/test_fs/fstest_defs.py13
-rw-r--r--test/py/tests/test_fs/test_basic.py287
-rw-r--r--test/py/tests/test_fs/test_ext.py224
-rw-r--r--test/py/tests/test_fs/test_mkdir.py112
-rw-r--r--test/py/tests/test_fs/test_unlink.py109
-rw-r--r--test/unicode_ut.c543
81 files changed, 8024 insertions, 1226 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 64fb41e..ea21d59 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -383,12 +383,16 @@ T: git git://github.com/agraf/u-boot.git
F: doc/README.uefi
F: doc/README.iscsi
F: Documentation/efi.rst
+F: include/capitalization.h
+F: include/cp1250.h
+F: include/cp437.h
F: include/efi*
F: include/pe.h
F: include/asm-generic/pe.h
F: lib/charset.c
F: lib/efi*/
F: test/py/tests/test_efi*
+F: test/unicode_ut.c
F: cmd/bootefi.c
F: tools/file2include.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0f8dd32..b39d085 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -747,6 +747,8 @@ config ARCH_QEMU
select OF_CONTROL
select PL01X_SERIAL
imply CMD_DM
+ imply DM_RTC
+ imply RTC_PL031
config ARCH_RMOBILE
bool "Renesas ARM SoCs"
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c
index 052e070..be00bd5 100644
--- a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c
+++ b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c
@@ -835,7 +835,7 @@ int dram_init_banksize(void)
return 0;
}
-#if defined(CONFIG_EFI_LOADER) && !defined(CONFIG_SPL_BUILD)
+#if CONFIG_IS_ENABLED(EFI_LOADER)
void efi_add_known_memory(void)
{
int i;
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/fdt.c b/arch/arm/cpu/armv8/fsl-layerscape/fdt.c
index fc9de73..c9c2c3f 100644
--- a/arch/arm/cpu/armv8/fsl-layerscape/fdt.c
+++ b/arch/arm/cpu/armv8/fsl-layerscape/fdt.c
@@ -135,7 +135,7 @@ remove_psci_node:
fdt_add_mem_rsv(blob, (uintptr_t)&secondary_boot_code,
*boot_code_size);
-#if defined(CONFIG_EFI_LOADER) && !defined(CONFIG_SPL_BUILD)
+#if CONFIG_IS_ENABLED(EFI_LOADER)
efi_add_memory_map((uintptr_t)&secondary_boot_code,
ALIGN(*boot_code_size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT,
EFI_RESERVED_MEMORY_TYPE, false);
diff --git a/arch/arm/mach-tegra/board2.c b/arch/arm/mach-tegra/board2.c
index 421a71b..12257a4 100644
--- a/arch/arm/mach-tegra/board2.c
+++ b/arch/arm/mach-tegra/board2.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <dm.h>
+#include <efi_loader.h>
#include <errno.h>
#include <ns16550.h>
#include <usb.h>
@@ -210,6 +211,19 @@ int board_early_init_f(void)
int board_late_init(void)
{
+#if CONFIG_IS_ENABLED(EFI_LOADER)
+ if (gd->bd->bi_dram[1].start) {
+ /*
+ * Only bank 0 is below board_get_usable_ram_top(), so all of
+ * bank 1 is not mapped by the U-Boot MMU configuration, and so
+ * we must prevent EFI from using it.
+ */
+ efi_add_memory_map(gd->bd->bi_dram[1].start,
+ gd->bd->bi_dram[1].size >> EFI_PAGE_SHIFT,
+ EFI_BOOT_SERVICES_DATA, false);
+ }
+#endif
+
#if defined(CONFIG_TEGRA_SUPPORT_NON_SECURE)
if (tegra_cpu_is_non_secure()) {
printf("CPU is in NS mode\n");
diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c
index cde0b05..6098945 100644
--- a/arch/sandbox/cpu/cpu.c
+++ b/arch/sandbox/cpu/cpu.c
@@ -57,14 +57,104 @@ int cleanup_before_linux_select(int flags)
return 0;
}
+/**
+ * is_in_sandbox_mem() - Checks if a pointer is within sandbox's emulated DRAM
+ *
+ * This provides a way to check if a pointer is owned by sandbox (and is within
+ * its RAM) or not. Sometimes pointers come from a test which conceptually runs
+ * output sandbox, potentially with direct access to the C-library malloc()
+ * function, or the sandbox stack (which is not actually within the emulated
+ * DRAM.
+ *
+ * Such pointers obviously cannot be mapped into sandbox's DRAM, so we must
+ * detect them an process them separately, by recording a mapping to a tag,
+ * which we can use to map back to the pointer later.
+ *
+ * @ptr: Pointer to check
+ * @return true if this is within sandbox emulated DRAM, false if not
+ */
+static bool is_in_sandbox_mem(const void *ptr)
+{
+ return (const uint8_t *)ptr >= gd->arch.ram_buf &&
+ (const uint8_t *)ptr < gd->arch.ram_buf + gd->ram_size;
+}
+
+/**
+ * phys_to_virt() - Converts a sandbox RAM address to a pointer
+ *
+ * Sandbox uses U-Boot addresses from 0 to the size of DRAM. These index into
+ * the emulated DRAM buffer used by sandbox. This function converts such an
+ * address to a pointer into this buffer, which can be used to access the
+ * memory.
+ *
+ * If the address is outside this range, it is assumed to be a tag
+ */
void *phys_to_virt(phys_addr_t paddr)
{
- return (void *)(gd->arch.ram_buf + paddr);
+ struct sandbox_mapmem_entry *mentry;
+ struct sandbox_state *state;
+
+ /* If the address is within emulated DRAM, calculate the value */
+ if (paddr < gd->ram_size)
+ return (void *)(gd->arch.ram_buf + paddr);
+
+ /*
+ * Otherwise search out list of tags for the correct pointer previously
+ * created by map_to_sysmem()
+ */
+ state = state_get_current();
+ list_for_each_entry(mentry, &state->mapmem_head, sibling_node) {
+ if (mentry->tag == paddr) {
+ printf("%s: Used map from %lx to %p\n", __func__,
+ (ulong)paddr, mentry->ptr);
+ return mentry->ptr;
+ }
+ }
+
+ printf("%s: Cannot map sandbox address %lx (SDRAM from 0 to %lx)\n",
+ __func__, (ulong)paddr, (ulong)gd->ram_size);
+ os_abort();
+
+ /* Not reached */
+ return NULL;
+}
+
+struct sandbox_mapmem_entry *find_tag(const void *ptr)
+{
+ struct sandbox_mapmem_entry *mentry;
+ struct sandbox_state *state = state_get_current();
+
+ list_for_each_entry(mentry, &state->mapmem_head, sibling_node) {
+ if (mentry->ptr == ptr) {
+ debug("%s: Used map from %p to %lx\n", __func__, ptr,
+ mentry->tag);
+ return mentry;
+ }
+ }
+ return NULL;
}
-phys_addr_t virt_to_phys(void *vaddr)
+phys_addr_t virt_to_phys(void *ptr)
{
- return (phys_addr_t)((uint8_t *)vaddr - gd->arch.ram_buf);
+ struct sandbox_mapmem_entry *mentry;
+
+ /*
+ * If it is in emulated RAM, don't bother looking for a tag. Just
+ * calculate the pointer using the provides offset into the RAM buffer.
+ */
+ if (is_in_sandbox_mem(ptr))
+ return (phys_addr_t)((uint8_t *)ptr - gd->arch.ram_buf);
+
+ mentry = find_tag(ptr);
+ if (!mentry) {
+ /* Abort so that gdb can be used here */
+ printf("%s: Cannot map sandbox address %p (SDRAM from 0 to %lx)\n",
+ __func__, ptr, (ulong)gd->ram_size);
+ os_abort();
+ }
+ printf("%s: Used map from %p to %lx\n", __func__, ptr, mentry->tag);
+
+ return mentry->tag;
}
void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags)
@@ -87,24 +177,57 @@ void *map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags)
return phys_to_virt(paddr);
}
-void unmap_physmem(const void *vaddr, unsigned long flags)
+void unmap_physmem(const void *ptr, unsigned long flags)
{
#ifdef CONFIG_PCI
if (map_dev) {
- pci_unmap_physmem(vaddr, map_len, map_dev);
+ pci_unmap_physmem(ptr, map_len, map_dev);
map_dev = NULL;
}
#endif
}
-void sandbox_set_enable_pci_map(int enable)
+phys_addr_t map_to_sysmem(const void *ptr)
{
- enable_pci_map = enable;
+ struct sandbox_mapmem_entry *mentry;
+
+ /*
+ * If it is in emulated RAM, don't bother creating a tag. Just return
+ * the offset into the RAM buffer.
+ */
+ if (is_in_sandbox_mem(ptr))
+ return (u8 *)ptr - gd->arch.ram_buf;
+
+ /*
+ * See if there is an existing tag with this pointer. If not, set up a
+ * new one.
+ */
+ mentry = find_tag(ptr);
+ if (!mentry) {
+ struct sandbox_state *state = state_get_current();
+
+ mentry = malloc(sizeof(*mentry));
+ if (!mentry) {
+ printf("%s: Error: Out of memory\n", __func__);
+ os_exit(ENOMEM);
+ }
+ mentry->tag = state->next_tag++;
+ mentry->ptr = (void *)ptr;
+ list_add_tail(&mentry->sibling_node, &state->mapmem_head);
+ debug("%s: Added map from %p to %lx\n", __func__, ptr,
+ (ulong)mentry->tag);
+ }
+
+ /*
+ * Return the tag as the address to use. A later call to map_sysmem()
+ * will return ptr
+ */
+ return mentry->tag;
}
-phys_addr_t map_to_sysmem(const void *ptr)
+void sandbox_set_enable_pci_map(int enable)
{
- return (u8 *)ptr - gd->arch.ram_buf;
+ enable_pci_map = enable;
}
void flush_dcache_range(unsigned long start, unsigned long stop)
@@ -165,15 +288,3 @@ ulong timer_get_boot_us(void)
return (count - base_count) / 1000;
}
-
-int setjmp(jmp_buf jmp)
-{
- return os_setjmp((ulong *)jmp, sizeof(*jmp));
-}
-
-void longjmp(jmp_buf jmp, int ret)
-{
- os_longjmp((ulong *)jmp, ret);
- while (1)
- ;
-}
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
index 5839932..9fbcb9e 100644
--- a/arch/sandbox/cpu/os.c
+++ b/arch/sandbox/cpu/os.c
@@ -143,14 +143,16 @@ void os_tty_raw(int fd, bool allow_sigs)
void *os_malloc(size_t length)
{
struct os_mem_hdr *hdr;
+ int page_size = getpagesize();
- hdr = mmap(NULL, length + sizeof(*hdr), PROT_READ | PROT_WRITE,
+ hdr = mmap(NULL, length + page_size,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (hdr == MAP_FAILED)
return NULL;
hdr->length = length;
- return hdr + 1;
+ return (void *)hdr + page_size;
}
void os_free(void *ptr)
@@ -630,24 +632,7 @@ void os_localtime(struct rtc_time *rt)
rt->tm_isdst = tm->tm_isdst;
}
-int os_setjmp(ulong *jmp, int size)
+void os_abort(void)
{
- jmp_buf dummy;
-
- /*
- * We cannot rely on the struct name that jmp_buf uses, so use a
- * local variable here
- */
- if (size < sizeof(dummy)) {
- printf("setjmp: jmpbuf is too small (%d bytes, need %d)\n",
- size, sizeof(jmp_buf));
- return -ENOSPC;
- }
-
- return setjmp((struct __jmp_buf_tag *)jmp);
-}
-
-void os_longjmp(ulong *jmp, int ret)
-{
- longjmp((struct __jmp_buf_tag *)jmp, ret);
+ abort();
}
diff --git a/arch/sandbox/cpu/state.c b/arch/sandbox/cpu/state.c
index cc50819..04a11fe 100644
--- a/arch/sandbox/cpu/state.c
+++ b/arch/sandbox/cpu/state.c
@@ -359,6 +359,14 @@ void state_reset_for_test(struct sandbox_state *state)
memset(&state->wdt, '\0', sizeof(state->wdt));
memset(state->spi, '\0', sizeof(state->spi));
+
+ /*
+ * Set up the memory tag list. Use the top of emulated SDRAM for the
+ * first tag number, since that address offset is outside the legal
+ * range, and can be assumed to be a tag.
+ */
+ INIT_LIST_HEAD(&state->mapmem_head);
+ state->next_tag = state->ram_size;
}
int state_init(void)
diff --git a/arch/sandbox/include/asm/setjmp.h b/arch/sandbox/include/asm/setjmp.h
index 1fe37c9..001c7ea 100644
--- a/arch/sandbox/include/asm/setjmp.h
+++ b/arch/sandbox/include/asm/setjmp.h
@@ -24,6 +24,11 @@ struct jmp_buf_data {
typedef struct jmp_buf_data jmp_buf[1];
+/*
+ * We have to directly link with the system versions of
+ * setjmp/longjmp, because setjmp must not return as otherwise
+ * the stack may become invalid.
+ */
int setjmp(jmp_buf jmp);
__noreturn void longjmp(jmp_buf jmp, int ret);
diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h
index 7ed4b51..a612ce8 100644
--- a/arch/sandbox/include/asm/state.h
+++ b/arch/sandbox/include/asm/state.h
@@ -9,6 +9,7 @@
#include <config.h>
#include <sysreset.h>
#include <stdbool.h>
+#include <linux/list.h>
#include <linux/stringify.h>
/**
@@ -45,6 +46,23 @@ struct sandbox_wdt_info {
bool running;
};
+/**
+ * struct sandbox_mapmem_entry - maps pointers to/from U-Boot addresses
+ *
+ * When map_to_sysmem() is called with an address outside sandbox's emulated
+ * RAM, a record is created with a tag that can be used to reference that
+ * pointer. When map_sysmem() is called later with that tag, the pointer will
+ * be returned, just as it would for a normal sandbox address.
+ *
+ * @tag: Address tag (a value which U-Boot uses to refer to the address)
+ * @ptr: Associated pointer for that tag
+ */
+struct sandbox_mapmem_entry {
+ ulong tag;
+ void *ptr;
+ struct list_head sibling_node;
+};
+
/* The complete state of the test system */
struct sandbox_state {
const char *cmd; /* Command to execute */
@@ -78,6 +96,9 @@ struct sandbox_state {
/* Information about Watchdog */
struct sandbox_wdt_info wdt;
+
+ ulong next_tag; /* Next address tag to allocate */
+ struct list_head mapmem_head; /* struct sandbox_mapmem_entry */
};
/* Minimum space we guarantee in the state FDT when calling read/write*/
diff --git a/arch/x86/lib/e820.c b/arch/x86/lib/e820.c
index 8b34f67..d6ae2c4 100644
--- a/arch/x86/lib/e820.c
+++ b/arch/x86/lib/e820.c
@@ -36,7 +36,7 @@ __weak unsigned int install_e820_map(unsigned int max_entries,
return 4;
}
-#if defined(CONFIG_EFI_LOADER) && !defined(CONFIG_SPL_BUILD)
+#if CONFIG_IS_ENABLED(EFI_LOADER)
void efi_add_known_memory(void)
{
struct e820_entry e820[E820MAX];
@@ -72,4 +72,4 @@ void efi_add_known_memory(void)
efi_add_memory_map(start, pages, type, false);
}
}
-#endif /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
+#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index b60c151..82d755c 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -49,6 +49,11 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS)
goto out;
+ /* Initialize root node */
+ ret = efi_root_node_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
/* Initialize EFI driver uclass */
ret = efi_driver_init();
if (ret != EFI_SUCCESS)
@@ -116,32 +121,47 @@ static void set_load_options(struct efi_loaded_image *loaded_image_info,
{
size_t size;
const char *env = env_get(env_var);
+ u16 *pos;
loaded_image_info->load_options = NULL;
loaded_image_info->load_options_size = 0;
if (!env)
return;
- size = strlen(env) + 1;
+ size = utf8_utf16_strlen(env) + 1;
loaded_image_info->load_options = calloc(size, sizeof(u16));
if (!loaded_image_info->load_options) {
printf("ERROR: Out of memory\n");
return;
}
- utf8_to_utf16(loaded_image_info->load_options, (u8 *)env, size);
+ pos = loaded_image_info->load_options;
+ utf8_utf16_strcpy(&pos, env);
loaded_image_info->load_options_size = size * 2;
}
-static void *copy_fdt(void *fdt)
+/**
+ * copy_fdt() - Copy the device tree to a new location available to EFI
+ *
+ * The FDT is relocated into a suitable location within the EFI memory map.
+ * An additional 12KB is added to the space in case the device tree needs to be
+ * expanded later with fdt_open_into().
+ *
+ * @fdt_addr: On entry, address of start of FDT. On exit, address of relocated
+ * FDT start
+ * @fdt_sizep: Returns new size of FDT, including
+ * @return new relocated address of FDT
+ */
+static efi_status_t copy_fdt(ulong *fdt_addrp, ulong *fdt_sizep)
{
- u64 fdt_size = fdt_totalsize(fdt);
unsigned long fdt_ram_start = -1L, fdt_pages;
+ efi_status_t ret = 0;
+ void *fdt, *new_fdt;
u64 new_fdt_addr;
- void *new_fdt;
+ uint fdt_size;
int i;
- for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
- u64 ram_start = gd->bd->bi_dram[i].start;
- u64 ram_size = gd->bd->bi_dram[i].size;
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ u64 ram_start = gd->bd->bi_dram[i].start;
+ u64 ram_size = gd->bd->bi_dram[i].size;
if (!ram_size)
continue;
@@ -154,30 +174,37 @@ static void *copy_fdt(void *fdt)
* Give us at least 4KB of breathing room in case the device tree needs
* to be expanded later. Round up to the nearest EFI page boundary.
*/
- fdt_size += 4096;
+ fdt = map_sysmem(*fdt_addrp, 0);
+ fdt_size = fdt_totalsize(fdt);
+ fdt_size += 4096 * 3;
fdt_size = ALIGN(fdt_size + EFI_PAGE_SIZE - 1, EFI_PAGE_SIZE);
fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
- /* Safe fdt location is at 128MB */
- new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
- if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
- EFI_RUNTIME_SERVICES_DATA, fdt_pages,
- &new_fdt_addr) != EFI_SUCCESS) {
+ /* Safe fdt location is at 127MB */
+ new_fdt_addr = fdt_ram_start + (127 * 1024 * 1024) + fdt_size;
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_RUNTIME_SERVICES_DATA, fdt_pages,
+ &new_fdt_addr);
+ if (ret != EFI_SUCCESS) {
/* If we can't put it there, put it somewhere */
new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
- if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
- EFI_RUNTIME_SERVICES_DATA, fdt_pages,
- &new_fdt_addr) != EFI_SUCCESS) {
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_RUNTIME_SERVICES_DATA, fdt_pages,
+ &new_fdt_addr);
+ if (ret != EFI_SUCCESS) {
printf("ERROR: Failed to reserve space for FDT\n");
- return NULL;
+ goto done;
}
}
- new_fdt = (void*)(ulong)new_fdt_addr;
+ new_fdt = map_sysmem(new_fdt_addr, fdt_size);
memcpy(new_fdt, fdt, fdt_totalsize(fdt));
fdt_set_totalsize(new_fdt, fdt_size);
- return new_fdt;
+ *fdt_addrp = new_fdt_addr;
+ *fdt_sizep = fdt_size;
+done:
+ return ret;
}
static efi_status_t efi_do_enter(
@@ -250,22 +277,27 @@ static void efi_carve_out_dt_rsv(void *fdt)
}
}
-static efi_status_t efi_install_fdt(void *fdt)
+static efi_status_t efi_install_fdt(ulong fdt_addr)
{
bootm_headers_t img = { 0 };
- ulong fdt_pages, fdt_size, fdt_start, fdt_end;
+ ulong fdt_pages, fdt_size, fdt_start;
efi_status_t ret;
+ void *fdt;
+ fdt = map_sysmem(fdt_addr, 0);
if (fdt_check_header(fdt)) {
printf("ERROR: invalid device tree\n");
return EFI_INVALID_PARAMETER;
}
/* Prepare fdt for payload */
- fdt = copy_fdt(fdt);
- if (!fdt)
- return EFI_OUT_OF_RESOURCES;
+ ret = copy_fdt(&fdt_addr, &fdt_size);
+ if (ret)
+ return ret;
+ unmap_sysmem(fdt);
+ fdt = map_sysmem(fdt_addr, 0);
+ fdt_size = fdt_totalsize(fdt);
if (image_setup_libfdt(&img, fdt, 0, NULL)) {
printf("ERROR: failed to process device tree\n");
return EFI_LOAD_ERROR;
@@ -279,30 +311,35 @@ static efi_status_t efi_install_fdt(void *fdt)
return EFI_OUT_OF_RESOURCES;
/* And reserve the space in the memory map */
- fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
- fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
- fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
+ fdt_start = fdt_addr;
fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
- /* Give a bootloader the chance to modify the device tree */
- fdt_pages += 2;
+
ret = efi_add_memory_map(fdt_start, fdt_pages,
EFI_BOOT_SERVICES_DATA, true);
+
return ret;
}
-/*
- * Load an EFI payload into a newly allocated piece of memory, register all
- * EFI objects it would want to access and jump to it.
+/**
+ * do_bootefi_exec() - execute EFI binary
+ *
+ * @efi: address of the binary
+ * @device_path: path of the device from which the binary was loaded
+ * @image_path: device path of the binary
+ * Return: status code
+ *
+ * Load the EFI binary into a newly assigned memory unwinding the relocation
+ * information, install the loaded image protocol, and call the binary.
*/
static efi_status_t do_bootefi_exec(void *efi,
struct efi_device_path *device_path,
struct efi_device_path *image_path)
{
- struct efi_loaded_image loaded_image_info = {};
- struct efi_object loaded_image_info_obj = {};
- struct efi_object mem_obj = {};
+ efi_handle_t mem_handle = NULL;
struct efi_device_path *memdp = NULL;
efi_status_t ret;
+ struct efi_loaded_image_obj *image_handle = NULL;
+ struct efi_loaded_image *loaded_image_info = NULL;
EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
struct efi_system_table *st);
@@ -310,16 +347,21 @@ static efi_status_t do_bootefi_exec(void *efi,
/*
* Special case for efi payload not loaded from disk, such as
* 'bootefi hello' or for example payload loaded directly into
- * memory via jtag/etc:
+ * memory via jtag, etc:
*/
if (!device_path && !image_path) {
printf("WARNING: using memory device/image path, this may confuse some payloads!\n");
/* actual addresses filled in after efi_load_pe() */
memdp = efi_dp_from_mem(0, 0, 0);
device_path = image_path = memdp;
- efi_add_handle(&mem_obj);
-
- ret = efi_add_protocol(mem_obj.handle, &efi_guid_device_path,
+ /*
+ * Grub expects that the device path of the loaded image is
+ * installed on a handle.
+ */
+ ret = efi_create_handle(&mem_handle);
+ if (ret != EFI_SUCCESS)
+ goto exit;
+ ret = efi_add_protocol(mem_handle, &efi_guid_device_path,
device_path);
if (ret != EFI_SUCCESS)
goto exit;
@@ -327,8 +369,10 @@ static efi_status_t do_bootefi_exec(void *efi,
assert(device_path && image_path);
}
- efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
- device_path, image_path);
+ ret = efi_setup_loaded_image(device_path, image_path, &image_handle,
+ &loaded_image_info);
+ if (ret != EFI_SUCCESS)
+ goto exit;
/*
* gd lives in a fixed register which may get clobbered while we execute
@@ -337,9 +381,9 @@ static efi_status_t do_bootefi_exec(void *efi,
efi_save_gd();
/* Transfer environment variable bootargs as load options */
- set_load_options(&loaded_image_info, "bootargs");
+ set_load_options(loaded_image_info, "bootargs");
/* Load the EFI payload */
- entry = efi_load_pe(efi, &loaded_image_info);
+ entry = efi_load_pe(image_handle, efi, loaded_image_info);
if (!entry) {
ret = EFI_LOAD_ERROR;
goto exit;
@@ -347,10 +391,10 @@ static efi_status_t do_bootefi_exec(void *efi,
if (memdp) {
struct efi_device_path_memory *mdp = (void *)memdp;
- mdp->memory_type = loaded_image_info.image_code_type;
- mdp->start_address = (uintptr_t)loaded_image_info.image_base;
+ mdp->memory_type = loaded_image_info->image_code_type;
+ mdp->start_address = (uintptr_t)loaded_image_info->image_base;
mdp->end_address = mdp->start_address +
- loaded_image_info.image_size;
+ loaded_image_info->image_size;
}
/* we don't support much: */
@@ -360,8 +404,8 @@ static efi_status_t do_bootefi_exec(void *efi,
/* Call our payload! */
debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
- if (setjmp(&loaded_image_info.exit_jmp)) {
- ret = loaded_image_info.exit_status;
+ if (setjmp(&image_handle->exit_jmp)) {
+ ret = image_handle->exit_status;
goto exit;
}
@@ -373,7 +417,7 @@ static efi_status_t do_bootefi_exec(void *efi,
/* Move into EL2 and keep running there */
armv8_switch_to_el2((ulong)entry,
- (ulong)&loaded_image_info_obj.handle,
+ (ulong)image_handle,
(ulong)&systab, 0, (ulong)efi_run_in_el2,
ES_TO_AARCH64);
@@ -390,7 +434,7 @@ static efi_status_t do_bootefi_exec(void *efi,
secure_ram_addr(_do_nonsec_entry)(
efi_run_in_hyp,
(uintptr_t)entry,
- (uintptr_t)loaded_image_info_obj.handle,
+ (uintptr_t)image_handle,
(uintptr_t)&systab);
/* Should never reach here, efi exits with longjmp */
@@ -398,13 +442,14 @@ static efi_status_t do_bootefi_exec(void *efi,
}
#endif
- ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry);
+ ret = efi_do_enter(image_handle, &systab, entry);
exit:
/* image has returned, loaded-image obj goes *poof*: */
- list_del(&loaded_image_info_obj.link);
- if (mem_obj.handle)
- list_del(&mem_obj.link);
+ if (image_handle)
+ efi_delete_handle(&image_handle->parent);
+ if (mem_handle)
+ efi_delete_handle(mem_handle);
return ret;
}
@@ -443,7 +488,6 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
char *saddr;
efi_status_t r;
unsigned long fdt_addr;
- void *fdt;
/* Allow unaligned memory access */
allow_unaligned();
@@ -464,8 +508,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (!fdt_addr && *argv[2] != '0')
return CMD_RET_USAGE;
/* Install device tree */
- fdt = map_sysmem(fdt_addr, 0);
- r = efi_install_fdt(fdt);
+ r = efi_install_fdt(fdt_addr);
if (r != EFI_SUCCESS) {
printf("ERROR: failed to install device tree\n");
return CMD_RET_FAILURE;
@@ -489,8 +532,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
#endif
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
if (!strcmp(argv[1], "selftest")) {
- struct efi_loaded_image loaded_image_info = {};
- struct efi_object loaded_image_info_obj = {};
+ struct efi_loaded_image_obj *image_handle;
+ struct efi_loaded_image *loaded_image_info;
/* Construct a dummy device path. */
bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
@@ -498,9 +541,12 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
(uintptr_t)&efi_selftest);
bootefi_image_path = efi_dp_from_file(NULL, 0, "\\selftest");
- efi_setup_loaded_image(&loaded_image_info,
- &loaded_image_info_obj,
- bootefi_device_path, bootefi_image_path);
+ r = efi_setup_loaded_image(bootefi_device_path,
+ bootefi_image_path, &image_handle,
+ &loaded_image_info);
+ if (r != EFI_SUCCESS)
+ return CMD_RET_FAILURE;
+
/*
* gd lives in a fixed register which may get clobbered while we
* execute the payload. So save it here and restore it on every
@@ -508,12 +554,12 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
*/
efi_save_gd();
/* Transfer environment variable efi_selftest as load options */
- set_load_options(&loaded_image_info, "efi_selftest");
+ set_load_options(loaded_image_info, "efi_selftest");
/* Execute the test */
- r = efi_selftest(loaded_image_info_obj.handle, &systab);
+ r = efi_selftest(image_handle, &systab);
efi_restore_gd();
- free(loaded_image_info.load_options);
- list_del(&loaded_image_info_obj.link);
+ free(loaded_image_info->load_options);
+ efi_delete_handle(&image_handle->parent);
return r != EFI_SUCCESS;
} else
#endif
@@ -575,6 +621,13 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
char filename[32] = { 0 }; /* dp->str is u16[32] long */
char *s;
+ /* efi_set_bootdev is typically called repeatedly, recover memory */
+ efi_free_pool(bootefi_device_path);
+ efi_free_pool(bootefi_image_path);
+ /* If blk_get_device_part_str fails, avoid duplicate free. */
+ bootefi_device_path = NULL;
+ bootefi_image_path = NULL;
+
if (strcmp(dev, "Net")) {
struct blk_desc *desc;
disk_partition_t fs_partition;
diff --git a/cmd/fat.c b/cmd/fat.c
index 03de5d1..4b9a7ea 100644
--- a/cmd/fat.c
+++ b/cmd/fat.c
@@ -104,6 +104,7 @@ static int do_fat_fswrite(cmd_tbl_t *cmdtp, int flag,
int ret;
unsigned long addr;
unsigned long count;
+ long offset;
struct blk_desc *dev_desc = NULL;
disk_partition_t info;
int dev = 0;
@@ -126,9 +127,11 @@ static int do_fat_fswrite(cmd_tbl_t *cmdtp, int flag,
}
addr = simple_strtoul(argv[3], NULL, 16);
count = (argc <= 5) ? 0 : simple_strtoul(argv[5], NULL, 16);
+ /* offset should be a hex, but "-1" is allowed */
+ offset = (argc <= 6) ? 0 : simple_strtol(argv[6], NULL, 16);
buf = map_sysmem(addr, count);
- ret = file_fat_write(argv[4], buf, 0, count, &size);
+ ret = file_fat_write(argv[4], buf, offset, count, &size);
unmap_sysmem(buf);
if (ret < 0) {
printf("\n** Unable to write \"%s\" from %s %d:%d **\n",
@@ -142,10 +145,35 @@ static int do_fat_fswrite(cmd_tbl_t *cmdtp, int flag,
}
U_BOOT_CMD(
- fatwrite, 6, 0, do_fat_fswrite,
+ fatwrite, 7, 0, do_fat_fswrite,
"write file into a dos filesystem",
- "<interface> <dev[:part]> <addr> <filename> [<bytes>]\n"
+ "<interface> <dev[:part]> <addr> <filename> [<bytes> [<offset>]]\n"
" - write file 'filename' from the address 'addr' in RAM\n"
" to 'dev' on 'interface'"
);
+
+static int do_fat_rm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ return do_rm(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatrm, 4, 1, do_fat_rm,
+ "delete a file",
+ "<interface> [<dev[:part]>] <filename>\n"
+ " - delete a file from 'dev' on 'interface'"
+);
+
+static int do_fat_mkdir(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ return do_mkdir(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+ fatmkdir, 4, 1, do_fat_mkdir,
+ "create a directory",
+ "<interface> [<dev[:part]>] <directory>\n"
+ " - create a directory in 'dev' on 'interface'"
+);
#endif
diff --git a/common/bootm.c b/common/bootm.c
index e517d9f..8bf84eb 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -262,7 +262,7 @@ int bootm_find_images(int flag, int argc, char * const argv[])
puts("Could not find a valid device tree\n");
return 1;
}
- set_working_fdt_addr((ulong)images.ft_addr);
+ set_working_fdt_addr(map_to_sysmem(images.ft_addr));
#endif
#if IMAGE_ENABLE_FIT
diff --git a/common/image-fdt.c b/common/image-fdt.c
index 9b41f16..95748f0 100644
--- a/common/image-fdt.c
+++ b/common/image-fdt.c
@@ -193,7 +193,7 @@ int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size)
*of_flat_tree = of_start;
*of_size = of_len;
- set_working_fdt_addr((ulong)*of_flat_tree);
+ set_working_fdt_addr(map_to_sysmem(*of_flat_tree));
return 0;
error:
diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig
index 2df35a8..7fd726f 100644
--- a/configs/qemu_arm64_defconfig
+++ b/configs/qemu_arm64_defconfig
@@ -8,6 +8,7 @@ CONFIG_DISTRO_DEFAULTS=y
CONFIG_NR_DRAM_BANKS=1
# CONFIG_DISPLAY_CPUINFO is not set
# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_CMD_BOOTEFI_SELFTEST=y
CONFIG_CMD_PCI=y
CONFIG_CMD_USB=y
CONFIG_CMD_DATE=y
diff --git a/configs/qemu_arm_defconfig b/configs/qemu_arm_defconfig
index 2865599..fbceaf3 100644
--- a/configs/qemu_arm_defconfig
+++ b/configs/qemu_arm_defconfig
@@ -8,6 +8,7 @@ CONFIG_DISTRO_DEFAULTS=y
CONFIG_NR_DRAM_BANKS=1
# CONFIG_DISPLAY_CPUINFO is not set
# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_CMD_BOOTEFI_SELFTEST=y
CONFIG_CMD_PCI=y
CONFIG_CMD_USB=y
CONFIG_CMD_DATE=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ca12118..0b20968 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -23,6 +23,7 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y
CONFIG_CMD_CPU=y
CONFIG_CMD_LICENSE=y
CONFIG_CMD_BOOTZ=y
+CONFIG_CMD_BOOTEFI_SELFTEST=y
# CONFIG_CMD_ELF is not set
CONFIG_CMD_ASKENV=y
CONFIG_CMD_GREPENV=y
diff --git a/configs/vf610twr_defconfig b/configs/vf610twr_defconfig
index 59066a3..3f38c88 100644
--- a/configs/vf610twr_defconfig
+++ b/configs/vf610twr_defconfig
@@ -39,3 +39,4 @@ CONFIG_PHY_MICREL=y
CONFIG_MII=y
CONFIG_DM_SERIAL=y
CONFIG_FSL_LPUART=y
+# CONFIG_EFI_UNICODE_CAPITALIZATION is not set
diff --git a/configs/vf610twr_nand_defconfig b/configs/vf610twr_nand_defconfig
index 5b269fd..d6e318f 100644
--- a/configs/vf610twr_nand_defconfig
+++ b/configs/vf610twr_nand_defconfig
@@ -39,3 +39,4 @@ CONFIG_PHY_MICREL=y
CONFIG_MII=y
CONFIG_DM_SERIAL=y
CONFIG_FSL_LPUART=y
+# CONFIG_EFI_UNICODE_CAPITALIZATION is not set
diff --git a/drivers/rtc/pl031.c b/drivers/rtc/pl031.c
index 8955805..8bf04f2 100644
--- a/drivers/rtc/pl031.c
+++ b/drivers/rtc/pl031.c
@@ -8,13 +8,11 @@
#include <common.h>
#include <command.h>
+#include <dm.h>
+#include <errno.h>
#include <rtc.h>
-
-#if defined(CONFIG_CMD_DATE)
-
-#ifndef CONFIG_SYS_RTC_PL031_BASE
-#error CONFIG_SYS_RTC_PL031_BASE is not defined!
-#endif
+#include <asm/io.h>
+#include <asm/types.h>
/*
* Register definitions
@@ -30,78 +28,114 @@
#define RTC_CR_START (1 << 0)
-#define RTC_WRITE_REG(addr, val) \
- (*(volatile unsigned int *)(CONFIG_SYS_RTC_PL031_BASE + (addr)) = (val))
-#define RTC_READ_REG(addr) \
- (*(volatile unsigned int *)(CONFIG_SYS_RTC_PL031_BASE + (addr)))
+struct pl031_platdata {
+ phys_addr_t base;
+};
-static int pl031_initted = 0;
+static inline u32 pl031_read_reg(struct udevice *dev, int reg)
+{
+ struct pl031_platdata *pdata = dev_get_platdata(dev);
-/* Enable RTC Start in Control register*/
-void rtc_init(void)
+ return readl(pdata->base + reg);
+}
+
+static inline u32 pl031_write_reg(struct udevice *dev, int reg, u32 value)
{
- RTC_WRITE_REG(RTC_CR, RTC_CR_START);
+ struct pl031_platdata *pdata = dev_get_platdata(dev);
- pl031_initted = 1;
+ return writel(value, pdata->base + reg);
}
/*
- * Reset the RTC. We set the date back to 1970-01-01.
+ * Probe RTC device
+ */
+static int pl031_probe(struct udevice *dev)
+{
+ /* Enable RTC Start in Control register*/
+ pl031_write_reg(dev, RTC_CR, RTC_CR_START);
+
+ return 0;
+}
+
+/*
+ * Get the current time from the RTC
*/
-void rtc_reset(void)
+static int pl031_get(struct udevice *dev, struct rtc_time *tm)
{
- RTC_WRITE_REG(RTC_LR, 0x00);
- if(!pl031_initted)
- rtc_init();
+ unsigned long tim;
+
+ if (!tm)
+ return -EINVAL;
+
+ tim = pl031_read_reg(dev, RTC_DR);
+
+ rtc_to_tm(tim, tm);
+
+ debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
}
/*
* Set the RTC
-*/
-int rtc_set(struct rtc_time *tmp)
+ */
+static int pl031_set(struct udevice *dev, const struct rtc_time *tm)
{
unsigned long tim;
- if(!pl031_initted)
- rtc_init();
+ if (!tm)
+ return -EINVAL;
- if (tmp == NULL) {
- puts("Error setting the date/time\n");
- return -1;
- }
+ debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
/* Calculate number of seconds this incoming time represents */
- tim = rtc_mktime(tmp);
+ tim = rtc_mktime(tm);
- RTC_WRITE_REG(RTC_LR, tim);
+ pl031_write_reg(dev, RTC_LR, tim);
- return -1;
+ return 0;
}
/*
- * Get the current time from the RTC
+ * Reset the RTC. We set the date back to 1970-01-01.
*/
-int rtc_get(struct rtc_time *tmp)
+static int pl031_reset(struct udevice *dev)
{
- ulong tim;
+ pl031_write_reg(dev, RTC_LR, 0);
- if(!pl031_initted)
- rtc_init();
+ return 0;
+}
- if (tmp == NULL) {
- puts("Error getting the date/time\n");
- return -1;
- }
+static const struct rtc_ops pl031_ops = {
+ .get = pl031_get,
+ .set = pl031_set,
+ .reset = pl031_reset,
+};
- tim = RTC_READ_REG(RTC_DR);
+static const struct udevice_id pl031_ids[] = {
+ { .compatible = "arm,pl031" },
+ { }
+};
- rtc_to_tm(tim, tmp);
+static int pl031_ofdata_to_platdata(struct udevice *dev)
+{
+ struct pl031_platdata *pdata = dev_get_platdata(dev);
- debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
- tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ pdata->base = dev_read_addr(dev);
return 0;
}
-#endif
+U_BOOT_DRIVER(rtc_pl031) = {
+ .name = "rtc-pl031",
+ .id = UCLASS_RTC,
+ .of_match = pl031_ids,
+ .probe = pl031_probe,
+ .ofdata_to_platdata = pl031_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct pl031_platdata),
+ .ops = &pl031_ops,
+};
diff --git a/drivers/serial/serial_efi.c b/drivers/serial/serial_efi.c
index 399dfd6..1b54d18 100644
--- a/drivers/serial/serial_efi.c
+++ b/drivers/serial/serial_efi.c
@@ -17,7 +17,7 @@
/* Information about the efi console */
struct serial_efi_priv {
- struct efi_simple_input_interface *con_in;
+ struct efi_simple_text_input_protocol *con_in;
struct efi_simple_text_output_protocol *con_out;
struct efi_input_key key;
bool have_key;
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index f1d3ad3..0c36a5d 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -213,6 +213,14 @@ static void vidconsole_escape_char(struct udevice *dev, char ch)
s++; /* ; */
s = parsenum(s, &col);
+ /*
+ * Ensure we stay in the bounds of the screen.
+ */
+ if (row >= priv->rows)
+ row = priv->rows - 1;
+ if (col >= priv->cols)
+ col = priv->cols - 1;
+
priv->ycur = row * priv->y_charsize;
priv->xcur_frac = priv->xstart_frac +
VID_TO_POS(col * priv->x_charsize);
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 4b722fc..b08949d 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -260,7 +260,7 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
- printf("FAT: Misaligned buffer address (%p)\n", buffer);
+ debug("FAT: Misaligned buffer address (%p)\n", buffer);
while (size >= mydata->sect_size) {
ret = disk_read(startsect++, 1, tmpbuf);
@@ -465,15 +465,6 @@ static __u8 mkcksum(const char name[8], const char ext[3])
}
/*
- * TODO these should go away once fat_write is reworked to use the
- * directory iterator
- */
-__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
- __aligned(ARCH_DMA_MINALIGN);
-__u8 do_fat_read_at_block[MAX_CLUSTSIZE]
- __aligned(ARCH_DMA_MINALIGN);
-
-/*
* Read boot sector and volume info from a FAT filesystem
*/
static int
@@ -558,10 +549,17 @@ static int get_fs_info(fsdata *mydata)
if (mydata->fatsize == 32) {
mydata->fatlength = bs.fat32_length;
+ mydata->total_sect = bs.total_sect;
} else {
mydata->fatlength = bs.fat_length;
+ mydata->total_sect = (bs.sectors[1] << 8) + bs.sectors[0];
+ if (!mydata->total_sect)
+ mydata->total_sect = bs.total_sect;
}
+ if (!mydata->total_sect) /* unlikely */
+ mydata->total_sect = (u32)cur_part_info.size;
+ mydata->fats = bs.fats;
mydata->fat_sect = bs.reserved;
mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
@@ -633,7 +631,9 @@ static int get_fs_info(fsdata *mydata)
typedef struct {
fsdata *fsdata; /* filesystem parameters */
+ unsigned start_clust; /* first cluster */
unsigned clust; /* current cluster */
+ unsigned next_clust; /* next cluster if remaining == 0 */
int last_cluster; /* set once we've read last cluster */
int is_root; /* is iterator at root directory */
int remaining; /* remaining dent's in current cluster */
@@ -664,7 +664,9 @@ static int fat_itr_root(fat_itr *itr, fsdata *fsdata)
return -ENXIO;
itr->fsdata = fsdata;
+ itr->start_clust = 0;
itr->clust = fsdata->root_cluster;
+ itr->next_clust = fsdata->root_cluster;
itr->dent = NULL;
itr->remaining = 0;
itr->last_cluster = 0;
@@ -698,11 +700,14 @@ static void fat_itr_child(fat_itr *itr, fat_itr *parent)
assert(fat_itr_isdir(parent));
itr->fsdata = parent->fsdata;
+ itr->start_clust = clustnum;
if (clustnum > 0) {
itr->clust = clustnum;
+ itr->next_clust = clustnum;
itr->is_root = 0;
} else {
itr->clust = parent->fsdata->root_cluster;
+ itr->next_clust = parent->fsdata->root_cluster;
itr->is_root = 1;
}
itr->dent = NULL;
@@ -720,7 +725,7 @@ static void *next_cluster(fat_itr *itr)
if (itr->last_cluster)
return NULL;
- sect = clust_to_sect(itr->fsdata, itr->clust);
+ sect = clust_to_sect(itr->fsdata, itr->next_clust);
debug("FAT read(sect=%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
sect, itr->fsdata->clust_size, DIRENTSPERBLOCK);
@@ -741,18 +746,19 @@ static void *next_cluster(fat_itr *itr)
return NULL;
}
+ itr->clust = itr->next_clust;
if (itr->is_root && itr->fsdata->fatsize != 32) {
- itr->clust++;
- sect = clust_to_sect(itr->fsdata, itr->clust);
+ itr->next_clust++;
+ sect = clust_to_sect(itr->fsdata, itr->next_clust);
if (sect - itr->fsdata->rootdir_sect >=
itr->fsdata->rootdir_size) {
- debug("cursect: 0x%x\n", itr->clust);
+ debug("nextclust: 0x%x\n", itr->next_clust);
itr->last_cluster = 1;
}
} else {
- itr->clust = get_fatent(itr->fsdata, itr->clust);
- if (CHECK_CLUST(itr->clust, itr->fsdata->fatsize)) {
- debug("cursect: 0x%x\n", itr->clust);
+ itr->next_clust = get_fatent(itr->fsdata, itr->next_clust);
+ if (CHECK_CLUST(itr->next_clust, itr->fsdata->fatsize)) {
+ debug("nextclust: 0x%x\n", itr->next_clust);
itr->last_cluster = 1;
}
}
@@ -768,8 +774,11 @@ static dir_entry *next_dent(fat_itr *itr)
itr->fsdata->clust_size;
/* have we reached the last cluster? */
- if (!dent)
+ if (!dent) {
+ /* a sign for no more entries left */
+ itr->dent = NULL;
return NULL;
+ }
itr->remaining = nbytes / sizeof(dir_entry) - 1;
itr->dent = dent;
@@ -924,6 +933,28 @@ static int fat_itr_resolve(fat_itr *itr, const char *path, unsigned type)
while (next[0] && !ISDIRDELIM(next[0]))
next++;
+ if (itr->is_root) {
+ /* root dir doesn't have "." nor ".." */
+ if ((((next - path) == 1) && !strncmp(path, ".", 1)) ||
+ (((next - path) == 2) && !strncmp(path, "..", 2))) {
+ /* point back to itself */
+ itr->clust = itr->fsdata->root_cluster;
+ itr->next_clust = itr->fsdata->root_cluster;
+ itr->dent = NULL;
+ itr->remaining = 0;
+ itr->last_cluster = 0;
+
+ if (next[0] == 0) {
+ if (type & TYPE_DIR)
+ return 0;
+ else
+ return -ENOENT;
+ }
+
+ return fat_itr_resolve(itr, next, type);
+ }
+ }
+
while (fat_itr_next(itr)) {
int match = 0;
unsigned n = max(strlen(itr->name), (size_t)(next - path));
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index 27e0ff6..fc211e7 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -99,7 +99,6 @@ static void set_name(dir_entry *dirent, const char *filename)
debug("ext : %s\n", dirent->ext);
}
-static __u8 num_of_fats;
/*
* Write fat buffer into block device
*/
@@ -128,7 +127,7 @@ static int flush_dirty_fat_buffer(fsdata *mydata)
return -1;
}
- if (num_of_fats == 2) {
+ if (mydata->fats == 2) {
/* Update corresponding second FAT blocks */
startblock += mydata->fatlength;
if (disk_write(startblock, getsize, bufptr) < 0) {
@@ -210,15 +209,14 @@ name11_12:
return 1;
}
-static int is_next_clust(fsdata *mydata, dir_entry *dentptr);
-static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
+static int flush_dir_table(fat_itr *itr);
/*
* Fill dir_slot entries with appropriate name, id, and attr
- * The real directory entry is returned by 'dentptr'
+ * 'itr' will point to a next entry
*/
-static void
-fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
+static int
+fill_dir_slot(fat_itr *itr, const char *l_name)
{
__u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
@@ -226,7 +224,7 @@ fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
int idx = 0, ret;
/* Get short file name checksum value */
- checksum = mkcksum((*dentptr)->name, (*dentptr)->ext);
+ checksum = mkcksum(itr->dent->name, itr->dent->ext);
do {
memset(slotptr, 0x00, sizeof(dir_slot));
@@ -241,120 +239,21 @@ fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
slotptr->id |= LAST_LONG_ENTRY_MASK;
while (counter >= 1) {
- if (is_next_clust(mydata, *dentptr)) {
- /* A new cluster is allocated for directory table */
- flush_dir_table(mydata, dentptr);
- }
- memcpy(*dentptr, slotptr, sizeof(dir_slot));
- (*dentptr)++;
+ memcpy(itr->dent, slotptr, sizeof(dir_slot));
slotptr--;
counter--;
- }
-
- if (is_next_clust(mydata, *dentptr)) {
- /* A new cluster is allocated for directory table */
- flush_dir_table(mydata, dentptr);
- }
-}
-
-static __u32 dir_curclust;
-
-/*
- * Extract the full long filename starting at 'retdent' (which is really
- * a slot) into 'l_name'. If successful also copy the real directory entry
- * into 'retdent'
- * If additional adjacent cluster for directory entries is read into memory,
- * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
- * the location of the real directory entry is returned by 'retdent'
- * Return 0 on success, -1 otherwise.
- */
-static int
-get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
- dir_entry **retdent, char *l_name)
-{
- dir_entry *realdent;
- dir_slot *slotptr = (dir_slot *)(*retdent);
- dir_slot *slotptr2 = NULL;
- __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
- PREFETCH_BLOCKS :
- mydata->clust_size);
- __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
- int idx = 0, cur_position = 0;
-
- if (counter > VFAT_MAXSEQ) {
- debug("Error: VFAT name is too long\n");
- return -1;
- }
-
- while ((__u8 *)slotptr < buflimit) {
- if (counter == 0)
- break;
- if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
- return -1;
- slotptr++;
- counter--;
- }
-
- if ((__u8 *)slotptr >= buflimit) {
- if (curclust == 0)
- return -1;
- curclust = get_fatent(mydata, dir_curclust);
- if (CHECK_CLUST(curclust, mydata->fatsize)) {
- debug("curclust: 0x%x\n", curclust);
- printf("Invalid FAT entry\n");
- return -1;
- }
-
- dir_curclust = curclust;
-
- if (get_cluster(mydata, curclust, get_contents_vfatname_block,
- mydata->clust_size * mydata->sect_size) != 0) {
- debug("Error: reading directory block\n");
- return -1;
- }
-
- slotptr2 = (dir_slot *)get_contents_vfatname_block;
- while (counter > 0) {
- if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
- & 0xff) != counter)
+ if (!fat_itr_next(itr))
+ if (!itr->dent && !itr->is_root && flush_dir_table(itr))
return -1;
- slotptr2++;
- counter--;
- }
-
- /* Save the real directory entry */
- realdent = (dir_entry *)slotptr2;
- while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
- slotptr2--;
- slot2str(slotptr2, l_name, &idx);
- }
- } else {
- /* Save the real directory entry */
- realdent = (dir_entry *)slotptr;
}
- do {
- slotptr--;
- if (slot2str(slotptr, l_name, &idx))
- break;
- } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
-
- l_name[idx] = '\0';
- if (*l_name == DELETED_FLAG)
- *l_name = '\0';
- else if (*l_name == aRING)
- *l_name = DELETED_FLAG;
- downcase(l_name, INT_MAX);
-
- /* Return the real directory entry */
- *retdent = realdent;
-
- if (slotptr2) {
- memcpy(get_dentfromdir_block, get_contents_vfatname_block,
- mydata->clust_size * mydata->sect_size);
- cur_position = (__u8 *)realdent - get_contents_vfatname_block;
- *retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
- }
+ if (!itr->dent && !itr->is_root)
+ /*
+ * don't care return value here because we have already
+ * finished completing an entry with name, only ending up
+ * no more entry left
+ */
+ flush_dir_table(itr);
return 0;
}
@@ -510,7 +409,7 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
- printf("FAT: Misaligned buffer address (%p)\n", buffer);
+ debug("FAT: Misaligned buffer address (%p)\n", buffer);
while (size >= mydata->sect_size) {
memcpy(tmpbuf, buffer, mydata->sect_size);
@@ -551,6 +450,121 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
return 0;
}
+static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
+
+/*
+ * Read and modify data on existing and consecutive cluster blocks
+ */
+static int
+get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
+ loff_t size, loff_t *gotsize)
+{
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 startsect;
+ loff_t wsize;
+ int clustcount, i, ret;
+
+ *gotsize = 0;
+ if (!size)
+ return 0;
+
+ assert(pos < bytesperclust);
+ startsect = clust_to_sect(mydata, clustnum);
+
+ debug("clustnum: %d, startsect: %d, pos: %lld\n",
+ clustnum, startsect, pos);
+
+ /* partial write at beginning */
+ if (pos) {
+ wsize = min(bytesperclust - pos, size);
+ ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(tmpbuf_cluster + pos, buffer, wsize);
+ ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+
+ startsect += mydata->clust_size;
+
+ if (!size)
+ return 0;
+ }
+
+ /* full-cluster write */
+ if (size >= bytesperclust) {
+ clustcount = lldiv(size, bytesperclust);
+
+ if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
+ wsize = clustcount * bytesperclust;
+ ret = disk_write(startsect,
+ clustcount * mydata->clust_size,
+ buffer);
+ if (ret != clustcount * mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+
+ startsect += clustcount * mydata->clust_size;
+ } else {
+ for (i = 0; i < clustcount; i++) {
+ memcpy(tmpbuf_cluster, buffer, bytesperclust);
+ ret = disk_write(startsect,
+ mydata->clust_size,
+ tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n",
+ ret);
+ return -1;
+ }
+
+ size -= bytesperclust;
+ buffer += bytesperclust;
+ *gotsize += bytesperclust;
+
+ startsect += mydata->clust_size;
+ }
+ }
+ }
+
+ /* partial write at end */
+ if (size) {
+ wsize = size;
+ ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+ memcpy(tmpbuf_cluster, buffer, wsize);
+ ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+ }
+
+ assert(!size);
+
+ return 0;
+}
+
/*
* Find the first empty cluster
*/
@@ -569,20 +583,20 @@ static int find_empty_cluster(fsdata *mydata)
}
/*
- * Write directory entries in 'get_dentfromdir_block' to block device
+ * Write directory entries in itr's buffer to block device
*/
-static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
+static int flush_dir_table(fat_itr *itr)
{
+ fsdata *mydata = itr->fsdata;
int dir_newclust = 0;
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
- if (set_cluster(mydata, dir_curclust,
- get_dentfromdir_block,
- mydata->clust_size * mydata->sect_size) != 0) {
- printf("error: wrinting directory entry\n");
- return;
+ if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) {
+ printf("error: writing directory entry\n");
+ return -1;
}
dir_newclust = find_empty_cluster(mydata);
- set_fatent_value(mydata, dir_curclust, dir_newclust);
+ set_fatent_value(mydata, itr->clust, dir_newclust);
if (mydata->fatsize == 32)
set_fatent_value(mydata, dir_newclust, 0xffffff8);
else if (mydata->fatsize == 16)
@@ -590,15 +604,19 @@ static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
else if (mydata->fatsize == 12)
set_fatent_value(mydata, dir_newclust, 0xff8);
- dir_curclust = dir_newclust;
+ itr->clust = dir_newclust;
+ itr->next_clust = dir_newclust;
if (flush_dirty_fat_buffer(mydata) < 0)
- return;
+ return -1;
- memset(get_dentfromdir_block, 0x00,
- mydata->clust_size * mydata->sect_size);
+ memset(itr->block, 0x00, bytesperclust);
- *dentptr = (dir_entry *) get_dentfromdir_block;
+ itr->dent = (dir_entry *)itr->block;
+ itr->last_cluster = 1;
+ itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
+
+ return 0;
}
/*
@@ -626,37 +644,212 @@ static int clear_fatent(fsdata *mydata, __u32 entry)
}
/*
+ * Set start cluster in directory entry
+ */
+static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
+ __u32 start_cluster)
+{
+ if (mydata->fatsize == 32)
+ dentptr->starthi =
+ cpu_to_le16((start_cluster & 0xffff0000) >> 16);
+ dentptr->start = cpu_to_le16(start_cluster & 0xffff);
+}
+
+/*
+ * Check whether adding a file makes the file system to
+ * exceed the size of the block device
+ * Return -1 when overflow occurs, otherwise return 0
+ */
+static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
+{
+ __u32 startsect, sect_num, offset;
+
+ if (clustnum > 0)
+ startsect = clust_to_sect(mydata, clustnum);
+ else
+ startsect = mydata->rootdir_sect;
+
+ sect_num = div_u64_rem(size, mydata->sect_size, &offset);
+
+ if (offset != 0)
+ sect_num++;
+
+ if (startsect + sect_num > total_sector)
+ return -1;
+ return 0;
+}
+
+/*
* Write at most 'maxsize' bytes from 'buffer' into
* the file associated with 'dentptr'
* Update the number of bytes written in *gotsize and return 0
* or return -1 on fatal errors.
*/
static int
-set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
- loff_t maxsize, loff_t *gotsize)
+set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
+ loff_t maxsize, loff_t *gotsize)
{
- loff_t filesize = FAT2CPU32(dentptr->size);
+ loff_t filesize;
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
__u32 curclust = START(dentptr);
__u32 endclust = 0, newclust = 0;
- loff_t actsize;
+ loff_t cur_pos, offset, actsize, wsize;
*gotsize = 0;
- debug("Filesize: %llu bytes\n", filesize);
-
- if (maxsize > 0 && filesize > maxsize)
- filesize = maxsize;
+ filesize = pos + maxsize;
debug("%llu bytes\n", filesize);
+ if (!filesize) {
+ if (!curclust)
+ return 0;
+ if (!CHECK_CLUST(curclust, mydata->fatsize) ||
+ IS_LAST_CLUST(curclust, mydata->fatsize)) {
+ clear_fatent(mydata, curclust);
+ set_start_cluster(mydata, dentptr, 0);
+ return 0;
+ }
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
if (!curclust) {
- if (filesize) {
- debug("error: nonempty clusterless file!\n");
+ assert(pos == 0);
+ goto set_clusters;
+ }
+
+ /* go to cluster at pos */
+ cur_pos = bytesperclust;
+ while (1) {
+ if (pos <= cur_pos)
+ break;
+ if (IS_LAST_CLUST(curclust, mydata->fatsize))
+ break;
+
+ newclust = get_fatent(mydata, curclust);
+ if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
+ CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
return -1;
}
+
+ cur_pos += bytesperclust;
+ curclust = newclust;
+ }
+ if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
+ assert(pos == cur_pos);
+ goto set_clusters;
+ }
+
+ assert(pos < cur_pos);
+ cur_pos -= bytesperclust;
+
+ /* overwrite */
+ assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
+ !CHECK_CLUST(curclust, mydata->fatsize));
+
+ while (1) {
+ /* search for allocated consecutive clusters */
+ actsize = bytesperclust;
+ endclust = curclust;
+ while (1) {
+ if (filesize <= (cur_pos + actsize))
+ break;
+
+ newclust = get_fatent(mydata, endclust);
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize))
+ break;
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ actsize += bytesperclust;
+ endclust = newclust;
+ }
+
+ /* overwrite to <curclust..endclust> */
+ if (pos < cur_pos)
+ offset = 0;
+ else
+ offset = pos - cur_pos;
+ wsize = min(cur_pos + actsize, filesize) - pos;
+ if (get_set_cluster(mydata, curclust, offset,
+ buffer, wsize, &actsize)) {
+ printf("Error get-and-setting cluster\n");
+ return -1;
+ }
+ buffer += wsize;
+ *gotsize += wsize;
+ cur_pos += offset + wsize;
+
+ if (filesize <= cur_pos)
+ break;
+
+ /* CHECK: newclust = get_fatent(mydata, endclust); */
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize))
+ /* no more clusters */
+ break;
+
+ curclust = newclust;
+ }
+
+ if (filesize <= cur_pos) {
+ /* no more write */
+ newclust = get_fatent(mydata, endclust);
+ if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
+ /* truncate the rest */
+ clear_fatent(mydata, newclust);
+
+ /* Mark end of file in FAT */
+ if (mydata->fatsize == 12)
+ newclust = 0xfff;
+ else if (mydata->fatsize == 16)
+ newclust = 0xffff;
+ else if (mydata->fatsize == 32)
+ newclust = 0xfffffff;
+ set_fatent_value(mydata, endclust, newclust);
+ }
+
return 0;
}
+ curclust = endclust;
+ filesize -= cur_pos;
+ assert(!(cur_pos % bytesperclust));
+
+set_clusters:
+ /* allocate and write */
+ assert(!pos);
+
+ /* Assure that curclust is valid */
+ if (!curclust) {
+ curclust = find_empty_cluster(mydata);
+ set_start_cluster(mydata, dentptr, curclust);
+ } else {
+ newclust = get_fatent(mydata, curclust);
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
+ newclust = determine_fatent(mydata, curclust);
+ set_fatent_value(mydata, curclust, newclust);
+ curclust = newclust;
+ } else {
+ debug("error: something wrong\n");
+ return -1;
+ }
+ }
+
+ /* TODO: already partially written */
+ if (check_overflow(mydata, curclust, filesize)) {
+ printf("Error: no space left: %llu\n", filesize);
+ return -1;
+ }
+
actsize = bytesperclust;
endclust = curclust;
do {
@@ -665,6 +858,7 @@ set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
newclust = determine_fatent(mydata, endclust);
if ((newclust - 1) != endclust)
+ /* write to <curclust..endclust> */
goto getit;
if (CHECK_CLUST(newclust, mydata->fatsize)) {
@@ -711,18 +905,8 @@ getit:
actsize = bytesperclust;
curclust = endclust = newclust;
} while (1);
-}
-/*
- * Set start cluster in directory entry
- */
-static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
- __u32 start_cluster)
-{
- if (mydata->fatsize == 32)
- dentptr->starthi =
- cpu_to_le16((start_cluster & 0xffff0000) >> 16);
- dentptr->start = cpu_to_le16(start_cluster & 0xffff);
+ return 0;
}
/*
@@ -740,334 +924,512 @@ static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
}
/*
- * Check whether adding a file makes the file system to
- * exceed the size of the block device
- * Return -1 when overflow occurs, otherwise return 0
+ * Find a directory entry based on filename or start cluster number
+ * If the directory entry is not found,
+ * the new position for writing a directory entry will be returned
*/
-static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
+static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
{
- __u32 startsect, sect_num, offset;
+ int match = 0;
- if (clustnum > 0) {
- startsect = clust_to_sect(mydata, clustnum);
- } else {
- startsect = mydata->rootdir_sect;
+ while (fat_itr_next(itr)) {
+ /* check both long and short name: */
+ if (!strcasecmp(filename, itr->name))
+ match = 1;
+ else if (itr->name != itr->s_name &&
+ !strcasecmp(filename, itr->s_name))
+ match = 1;
+
+ if (!match)
+ continue;
+
+ if (itr->dent->name[0] == '\0')
+ return NULL;
+ else
+ return itr->dent;
}
- sect_num = div_u64_rem(size, mydata->sect_size, &offset);
+ if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+ /* indicate that allocating dent failed */
+ itr->dent = NULL;
- if (offset != 0)
- sect_num++;
+ return NULL;
+}
+
+static int split_filename(char *filename, char **dirname, char **basename)
+{
+ char *p, *last_slash, *last_slash_cont;
+
+again:
+ p = filename;
+ last_slash = NULL;
+ last_slash_cont = NULL;
+ while (*p) {
+ if (ISDIRDELIM(*p)) {
+ last_slash = p;
+ last_slash_cont = p;
+ /* continuous slashes */
+ while (ISDIRDELIM(*p))
+ last_slash_cont = p++;
+ if (!*p)
+ break;
+ }
+ p++;
+ }
+
+ if (last_slash) {
+ if (last_slash_cont == (filename + strlen(filename) - 1)) {
+ /* remove trailing slashes */
+ *last_slash = '\0';
+ goto again;
+ }
+
+ if (last_slash == filename) {
+ /* avoid ""(null) directory */
+ *dirname = "/";
+ } else {
+ *last_slash = '\0';
+ *dirname = filename;
+ }
+
+ *last_slash_cont = '\0';
+ *basename = last_slash_cont + 1;
+ } else {
+ *dirname = "/"; /* root by default */
+ *basename = filename;
+ }
- if (startsect + sect_num > total_sector)
- return -1;
return 0;
}
-/*
- * Check if adding several entries exceed one cluster boundary
- */
-static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
+static int normalize_longname(char *l_filename, const char *filename)
{
- int cur_position;
+ const char *p, legal[] = "!#$%&\'()-.@^`_{}~";
+ char c;
+ int name_len;
- cur_position = (__u8 *)dentptr - get_dentfromdir_block;
+ /* Check that the filename is valid */
+ for (p = filename; p < filename + strlen(filename); p++) {
+ c = *p;
+
+ if (('0' <= c) && (c <= '9'))
+ continue;
+ if (('A' <= c) && (c <= 'Z'))
+ continue;
+ if (('a' <= c) && (c <= 'z'))
+ continue;
+ if (strchr(legal, c))
+ continue;
+ /* extended code */
+ if ((0x80 <= c) && (c <= 0xff))
+ continue;
- if (cur_position >= mydata->clust_size * mydata->sect_size)
- return 1;
- else
- return 0;
+ return -1;
+ }
+
+ /* Normalize it */
+ name_len = strlen(filename);
+ if (name_len >= VFAT_MAXLEN_BYTES)
+ /* should return an error? */
+ name_len = VFAT_MAXLEN_BYTES - 1;
+
+ memcpy(l_filename, filename, name_len);
+ l_filename[name_len] = 0; /* terminate the string */
+ downcase(l_filename, INT_MAX);
+
+ return 0;
}
-static dir_entry *empty_dentptr;
-/*
- * Find a directory entry based on filename or start cluster number
- * If the directory entry is not found,
- * the new position for writing a directory entry will be returned
- */
-static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
- char *filename, dir_entry *retdent, __u32 start)
+int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
+ loff_t size, loff_t *actwrite)
{
- __u32 curclust = sect_to_clust(mydata, startsect);
+ dir_entry *retdent;
+ fsdata datablock = { .fatbuf = NULL, };
+ fsdata *mydata = &datablock;
+ fat_itr *itr = NULL;
+ int ret = -1;
+ char *filename_copy, *parent, *basename;
+ char l_filename[VFAT_MAXLEN_BYTES];
- debug("get_dentfromdir: %s\n", filename);
+ debug("writing %s\n", filename);
- while (1) {
- dir_entry *dentptr;
+ filename_copy = strdup(filename);
+ if (!filename_copy)
+ return -ENOMEM;
- int i;
+ split_filename(filename_copy, &parent, &basename);
+ if (!strlen(basename)) {
+ ret = -EINVAL;
+ goto exit;
+ }
- if (get_cluster(mydata, curclust, get_dentfromdir_block,
- mydata->clust_size * mydata->sect_size) != 0) {
- printf("Error: reading directory block\n");
- return NULL;
- }
+ filename = basename;
+ if (normalize_longname(l_filename, filename)) {
+ printf("FAT: illegal filename (%s)\n", filename);
+ ret = -EINVAL;
+ goto exit;
+ }
- dentptr = (dir_entry *)get_dentfromdir_block;
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
- dir_curclust = curclust;
+ ret = fat_itr_root(itr, &datablock);
+ if (ret)
+ goto exit;
- for (i = 0; i < DIRENTSPERCLUST; i++) {
- char s_name[14], l_name[VFAT_MAXLEN_BYTES];
+ total_sector = datablock.total_sect;
- l_name[0] = '\0';
- if (dentptr->name[0] == DELETED_FLAG) {
- dentptr++;
- if (is_next_clust(mydata, dentptr))
- break;
- continue;
- }
- if ((dentptr->attr & ATTR_VOLUME)) {
- if ((dentptr->attr & ATTR_VFAT) &&
- (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
- get_long_file_name(mydata, curclust,
- get_dentfromdir_block,
- &dentptr, l_name);
- debug("vfatname: |%s|\n", l_name);
- } else {
- /* Volume label or VFAT entry */
- dentptr++;
- if (is_next_clust(mydata, dentptr))
- break;
- continue;
- }
- }
- if (dentptr->name[0] == 0) {
- debug("Dentname == NULL - %d\n", i);
- empty_dentptr = dentptr;
- return NULL;
- }
+ ret = fat_itr_resolve(itr, parent, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", parent, ret);
+ goto exit;
+ }
- get_name(dentptr, s_name);
+ retdent = find_directory_entry(itr, l_filename);
- if (strncasecmp(filename, s_name, sizeof(s_name)) &&
- strncasecmp(filename, l_name, sizeof(l_name))) {
- debug("Mismatch: |%s|%s|\n",
- s_name, l_name);
- dentptr++;
- if (is_next_clust(mydata, dentptr))
- break;
- continue;
- }
+ if (retdent) {
+ if (fat_itr_isdir(itr)) {
+ ret = -EISDIR;
+ goto exit;
+ }
- memcpy(retdent, dentptr, sizeof(dir_entry));
+ /* A file exists */
+ if (pos == -1)
+ /* Append to the end */
+ pos = FAT2CPU32(retdent->size);
+ if (pos > retdent->size) {
+ /* No hole allowed */
+ ret = -EINVAL;
+ goto exit;
+ }
- debug("DentName: %s", s_name);
- debug(", start: 0x%x", START(dentptr));
- debug(", size: 0x%x %s\n",
- FAT2CPU32(dentptr->size),
- (dentptr->attr & ATTR_DIR) ?
- "(DIR)" : "");
+ /* Update file size in a directory entry */
+ retdent->size = cpu_to_le32(pos + size);
+ } else {
+ /* Create a new file */
- return dentptr;
+ if (itr->is_root) {
+ /* root dir cannot have "." or ".." */
+ if (!strcmp(l_filename, ".") ||
+ !strcmp(l_filename, "..")) {
+ ret = -EINVAL;
+ goto exit;
+ }
}
- /*
- * In FAT16/12, the root dir is locate before data area, shows
- * in following:
- * -------------------------------------------------------------
- * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
- * -------------------------------------------------------------
- *
- * As a result if curclust is in Root dir, it is a negative
- * number or 0, 1.
- *
- */
- if (mydata->fatsize != 32 && (int)curclust <= 1) {
- /* Current clust is in root dir, set to next clust */
- curclust++;
- if ((int)curclust <= 1)
- continue; /* continue to find */
-
- /* Reach the end of root dir */
- empty_dentptr = dentptr;
- return NULL;
+ if (!itr->dent) {
+ printf("Error: allocating new dir entry\n");
+ ret = -EIO;
+ goto exit;
}
- curclust = get_fatent(mydata, dir_curclust);
- if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
- empty_dentptr = dentptr;
- return NULL;
+ if (pos) {
+ /* No hole allowed */
+ ret = -EINVAL;
+ goto exit;
}
- if (CHECK_CLUST(curclust, mydata->fatsize)) {
- debug("curclust: 0x%x\n", curclust);
- debug("Invalid FAT entry\n");
- return NULL;
+
+ memset(itr->dent, 0, sizeof(*itr->dent));
+
+ /* Set short name to set alias checksum field in dir_slot */
+ set_name(itr->dent, filename);
+ if (fill_dir_slot(itr, filename)) {
+ ret = -EIO;
+ goto exit;
}
+
+ /* Set attribute as archive for regular file */
+ fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
+
+ retdent = itr->dent;
}
- return NULL;
+ ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ ret = -EIO;
+ goto exit;
+ }
+ debug("attempt to write 0x%llx bytes\n", *actwrite);
+
+ /* Flush fat buffer */
+ ret = flush_dirty_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = set_cluster(mydata, itr->clust, itr->block,
+ mydata->clust_size * mydata->sect_size);
+ if (ret) {
+ printf("Error: writing directory entry\n");
+ ret = -EIO;
+ }
+
+exit:
+ free(filename_copy);
+ free(mydata->fatbuf);
+ free(itr);
+ return ret;
}
-static int do_fat_write(const char *filename, void *buffer, loff_t size,
- loff_t *actwrite)
+int file_fat_write(const char *filename, void *buffer, loff_t offset,
+ loff_t maxsize, loff_t *actwrite)
{
- dir_entry *dentptr, *retdent;
- __u32 startsect;
- __u32 start_cluster;
- boot_sector bs;
- volume_info volinfo;
- fsdata datablock;
- fsdata *mydata = &datablock;
- int cursect, i;
- int ret = -1, name_len;
- char l_filename[VFAT_MAXLEN_BYTES];
- char bad[2] = " ";
- const char illegal[] = "<>:\"/\\|?*";
+ return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
+}
- *actwrite = size;
- dir_curclust = 0;
+static int fat_dir_entries(fat_itr *itr)
+{
+ fat_itr *dirs;
+ fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
+ /* for FATBUFSIZE */
+ int count;
- if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
- debug("error: reading boot sector\n");
- return -1;
+ dirs = malloc_cache_aligned(sizeof(fat_itr));
+ if (!dirs) {
+ debug("Error: allocating memory\n");
+ count = -ENOMEM;
+ goto exit;
}
- total_sector = bs.total_sect;
- if (total_sector == 0)
- total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
+ /* duplicate fsdata */
+ fat_itr_child(dirs, itr);
+ fsdata = *dirs->fsdata;
- if (mydata->fatsize == 32)
- mydata->fatlength = bs.fat32_length;
- else
- mydata->fatlength = bs.fat_length;
+ /* allocate local fat buffer */
+ fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
+ if (!fsdata.fatbuf) {
+ debug("Error: allocating memory\n");
+ count = -ENOMEM;
+ goto exit;
+ }
+ fsdata.fatbufnum = -1;
+ dirs->fsdata = &fsdata;
- mydata->fat_sect = bs.reserved;
+ for (count = 0; fat_itr_next(dirs); count++)
+ ;
- cursect = mydata->rootdir_sect
- = mydata->fat_sect + mydata->fatlength * bs.fats;
- num_of_fats = bs.fats;
+exit:
+ free(fsdata.fatbuf);
+ free(dirs);
+ return count;
+}
- mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
- mydata->clust_size = bs.cluster_size;
+static int delete_dentry(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ dir_entry *dentptr = itr->dent;
- if (mydata->fatsize == 32) {
- mydata->data_begin = mydata->rootdir_sect -
- (mydata->clust_size * 2);
- } else {
- int rootdir_size;
-
- rootdir_size = ((bs.dir_entries[1] * (int)256 +
- bs.dir_entries[0]) *
- sizeof(dir_entry)) /
- mydata->sect_size;
- mydata->data_begin = mydata->rootdir_sect +
- rootdir_size -
- (mydata->clust_size * 2);
+ /* free cluster blocks */
+ clear_fatent(mydata, START(dentptr));
+ if (flush_dirty_fat_buffer(mydata) < 0) {
+ printf("Error: flush fat buffer\n");
+ return -EIO;
}
- mydata->fatbufnum = -1;
- mydata->fat_dirty = 0;
- mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
- if (mydata->fatbuf == NULL) {
- debug("Error: allocating memory\n");
- return -1;
+ /*
+ * update a directory entry
+ * TODO:
+ * - long file name support
+ * - find and mark the "new" first invalid entry as name[0]=0x00
+ */
+ memset(dentptr, 0, sizeof(*dentptr));
+ dentptr->name[0] = 0xe5;
+
+ if (set_cluster(mydata, itr->clust, itr->block,
+ mydata->clust_size * mydata->sect_size) != 0) {
+ printf("error: writing directory entry\n");
+ return -EIO;
}
- if (disk_read(cursect,
- (mydata->fatsize == 32) ?
- (mydata->clust_size) :
- PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
- debug("Error: reading rootdir block\n");
+ return 0;
+}
+
+int fat_unlink(const char *filename)
+{
+ fsdata fsdata = { .fatbuf = NULL, };
+ fat_itr *itr = NULL;
+ int n_entries, ret;
+ char *filename_copy, *dirname, *basename;
+
+ filename_copy = strdup(filename);
+ split_filename(filename_copy, &dirname, &basename);
+
+ if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
+ printf("Error: cannot remove root\n");
+ ret = -EINVAL;
goto exit;
}
- dentptr = (dir_entry *) do_fat_read_at_block;
- /* Strip leading (back-)slashes */
- while ISDIRDELIM(*filename)
- ++filename;
- /* Check that the filename is valid */
- for (i = 0; i < strlen(illegal); ++i) {
- *bad = illegal[i];
- if (strstr(filename, bad)) {
- printf("FAT: illegal filename (%s)\n", filename);
- return -1;
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ printf("Error: allocating memory\n");
+ return -ENOMEM;
+ }
+
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto exit;
+
+ total_sector = fsdata.total_sect;
+
+ ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", dirname, ret);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (!find_directory_entry(itr, basename)) {
+ printf("%s: doesn't exist\n", basename);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (fat_itr_isdir(itr)) {
+ n_entries = fat_dir_entries(itr);
+ if (n_entries < 0) {
+ ret = n_entries;
+ goto exit;
+ }
+ if (n_entries > 2) {
+ printf("Error: directory is not empty: %d\n",
+ n_entries);
+ ret = -EINVAL;
+ goto exit;
}
}
- name_len = strlen(filename);
- if (name_len >= VFAT_MAXLEN_BYTES)
- name_len = VFAT_MAXLEN_BYTES - 1;
+ ret = delete_dentry(itr);
- memcpy(l_filename, filename, name_len);
- l_filename[name_len] = 0; /* terminate the string */
- downcase(l_filename, INT_MAX);
+exit:
+ free(fsdata.fatbuf);
+ free(itr);
+ free(filename_copy);
- startsect = mydata->rootdir_sect;
- retdent = find_directory_entry(mydata, startsect,
- l_filename, dentptr, 0);
- if (retdent) {
- /* Update file size and start_cluster in a directory entry */
- retdent->size = cpu_to_le32(size);
- start_cluster = START(retdent);
-
- if (start_cluster) {
- if (size) {
- ret = check_overflow(mydata, start_cluster,
- size);
- if (ret) {
- printf("Error: %llu overflow\n", size);
- goto exit;
- }
- }
+ return ret;
+}
- ret = clear_fatent(mydata, start_cluster);
- if (ret) {
- printf("Error: clearing FAT entries\n");
- goto exit;
- }
+int fat_mkdir(const char *new_dirname)
+{
+ dir_entry *retdent;
+ fsdata datablock = { .fatbuf = NULL, };
+ fsdata *mydata = &datablock;
+ fat_itr *itr = NULL;
+ char *dirname_copy, *parent, *dirname;
+ char l_dirname[VFAT_MAXLEN_BYTES];
+ int ret = -1;
+ loff_t actwrite;
+ unsigned int bytesperclust;
+ dir_entry *dotdent = NULL;
+
+ dirname_copy = strdup(new_dirname);
+ if (!dirname_copy)
+ goto exit;
- if (!size)
- set_start_cluster(mydata, retdent, 0);
- } else if (size) {
- ret = start_cluster = find_empty_cluster(mydata);
- if (ret < 0) {
- printf("Error: finding empty cluster\n");
- goto exit;
- }
+ split_filename(dirname_copy, &parent, &dirname);
+ if (!strlen(dirname)) {
+ ret = -EINVAL;
+ goto exit;
+ }
- ret = check_overflow(mydata, start_cluster, size);
- if (ret) {
- printf("Error: %llu overflow\n", size);
- goto exit;
- }
+ if (normalize_longname(l_dirname, dirname)) {
+ printf("FAT: illegal filename (%s)\n", dirname);
+ ret = -EINVAL;
+ goto exit;
+ }
- set_start_cluster(mydata, retdent, start_cluster);
- }
- } else {
- /* Set short name to set alias checksum field in dir_slot */
- set_name(empty_dentptr, filename);
- fill_dir_slot(mydata, &empty_dentptr, filename);
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
- if (size) {
- ret = start_cluster = find_empty_cluster(mydata);
- if (ret < 0) {
- printf("Error: finding empty cluster\n");
- goto exit;
- }
+ ret = fat_itr_root(itr, &datablock);
+ if (ret)
+ goto exit;
+
+ total_sector = datablock.total_sect;
+
+ ret = fat_itr_resolve(itr, parent, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", parent, ret);
+ goto exit;
+ }
+
+ retdent = find_directory_entry(itr, l_dirname);
- ret = check_overflow(mydata, start_cluster, size);
- if (ret) {
- printf("Error: %llu overflow\n", size);
+ if (retdent) {
+ printf("%s: already exists\n", l_dirname);
+ ret = -EEXIST;
+ goto exit;
+ } else {
+ if (itr->is_root) {
+ /* root dir cannot have "." or ".." */
+ if (!strcmp(l_dirname, ".") ||
+ !strcmp(l_dirname, "..")) {
+ ret = -EINVAL;
goto exit;
}
- } else {
- start_cluster = 0;
}
- /* Set attribute as archieve for regular file */
- fill_dentry(mydata, empty_dentptr, filename,
- start_cluster, size, 0x20);
+ if (!itr->dent) {
+ printf("Error: allocating new dir entry\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ memset(itr->dent, 0, sizeof(*itr->dent));
+
+ /* Set short name to set alias checksum field in dir_slot */
+ set_name(itr->dent, dirname);
+ fill_dir_slot(itr, dirname);
+
+ /* Set attribute as archive for regular file */
+ fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
+ ATTR_DIR | ATTR_ARCH);
- retdent = empty_dentptr;
+ retdent = itr->dent;
}
- ret = set_contents(mydata, retdent, buffer, size, actwrite);
+ /* Default entries */
+ bytesperclust = mydata->clust_size * mydata->sect_size;
+ dotdent = malloc_cache_aligned(bytesperclust);
+ if (!dotdent) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ memset(dotdent, 0, bytesperclust);
+
+ memcpy(dotdent[0].name, ". ", 8);
+ memcpy(dotdent[0].ext, " ", 3);
+ dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
+
+ memcpy(dotdent[1].name, ".. ", 8);
+ memcpy(dotdent[1].ext, " ", 3);
+ dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
+ set_start_cluster(mydata, &dotdent[1], itr->start_clust);
+
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+ /* Write twice for "." */
+ set_start_cluster(mydata, &dotdent[0], START(retdent));
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
if (ret < 0) {
printf("Error: writing contents\n");
goto exit;
}
- debug("attempt to write 0x%llx bytes\n", *actwrite);
/* Flush fat buffer */
ret = flush_dirty_fat_buffer(mydata);
@@ -1077,24 +1439,15 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size,
}
/* Write directory table to device */
- ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block,
- mydata->clust_size * mydata->sect_size);
+ ret = set_cluster(mydata, itr->clust, itr->block,
+ mydata->clust_size * mydata->sect_size);
if (ret)
printf("Error: writing directory entry\n");
exit:
+ free(dirname_copy);
free(mydata->fatbuf);
+ free(itr);
+ free(dotdent);
return ret;
}
-
-int file_fat_write(const char *filename, void *buffer, loff_t offset,
- loff_t maxsize, loff_t *actwrite)
-{
- if (offset != 0) {
- printf("Error: non zero offset is currently not supported.\n");
- return -1;
- }
-
- printf("writing %s\n", filename);
- return do_fat_write(filename, buffer, maxsize, actwrite);
-}
diff --git a/fs/fs.c b/fs/fs.c
index cb68e81..adae98d 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -105,6 +105,16 @@ static inline int fs_opendir_unsupported(const char *filename,
return -EACCES;
}
+static inline int fs_unlink_unsupported(const char *filename)
+{
+ return -1;
+}
+
+static inline int fs_mkdir_unsupported(const char *dirname)
+{
+ return -1;
+}
+
struct fstype_info {
int fstype;
char *name;
@@ -142,6 +152,8 @@ struct fstype_info {
int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
/* see fs_closedir() */
void (*closedir)(struct fs_dir_stream *dirs);
+ int (*unlink)(const char *filename);
+ int (*mkdir)(const char *dirname);
};
static struct fstype_info fstypes[] = {
@@ -158,8 +170,12 @@ static struct fstype_info fstypes[] = {
.read = fat_read_file,
#ifdef CONFIG_FAT_WRITE
.write = file_fat_write,
+ .unlink = fat_unlink,
+ .mkdir = fat_mkdir,
#else
.write = fs_write_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
#endif
.uuid = fs_uuid_unsupported,
.opendir = fat_opendir,
@@ -185,6 +201,8 @@ static struct fstype_info fstypes[] = {
#endif
.uuid = ext4fs_uuid,
.opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
},
#endif
#ifdef CONFIG_SANDBOX
@@ -201,6 +219,8 @@ static struct fstype_info fstypes[] = {
.write = fs_write_sandbox,
.uuid = fs_uuid_unsupported,
.opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
},
#endif
#ifdef CONFIG_CMD_UBIFS
@@ -217,6 +237,8 @@ static struct fstype_info fstypes[] = {
.write = fs_write_unsupported,
.uuid = fs_uuid_unsupported,
.opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
},
#endif
#ifdef CONFIG_FS_BTRFS
@@ -233,6 +255,8 @@ static struct fstype_info fstypes[] = {
.write = fs_write_unsupported,
.uuid = btrfs_uuid,
.opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
},
#endif
{
@@ -248,6 +272,8 @@ static struct fstype_info fstypes[] = {
.write = fs_write_unsupported,
.uuid = fs_uuid_unsupported,
.opendir = fs_opendir_unsupported,
+ .unlink = fs_unlink_unsupported,
+ .mkdir = fs_mkdir_unsupported,
},
};
@@ -497,6 +523,33 @@ void fs_closedir(struct fs_dir_stream *dirs)
fs_close();
}
+int fs_unlink(const char *filename)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->unlink(filename);
+
+ fs_type = FS_TYPE_ANY;
+ fs_close();
+
+ return ret;
+}
+
+int fs_mkdir(const char *dirname)
+{
+ int ret;
+
+ struct fstype_info *info = fs_get_info(fs_type);
+
+ ret = info->mkdir(dirname);
+
+ fs_type = FS_TYPE_ANY;
+ fs_close();
+
+ return ret;
+}
int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
int fstype)
@@ -700,3 +753,37 @@ int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return CMD_RET_SUCCESS;
}
+int do_rm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
+ int fstype)
+{
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ if (fs_unlink(argv[3]))
+ return 1;
+
+ return 0;
+}
+
+int do_mkdir(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
+ int fstype)
+{
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ if (fs_set_blk_dev(argv[1], argv[2], fstype))
+ return 1;
+
+ ret = fs_mkdir(argv[3]);
+ if (ret) {
+ printf("** Unable to create a directory \"%s\" **\n", argv[3]);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/include/capitalization.h b/include/capitalization.h
new file mode 100644
index 0000000..9d7e8d2
--- /dev/null
+++ b/include/capitalization.h
@@ -0,0 +1,2028 @@
+/* SPDX-License-Identifier: Unicode-DFS-2016 */
+/*
+ * Capitalization tables
+ */
+
+struct capitalization_table {
+ u16 upper;
+ u16 lower;
+};
+
+/*
+ * Correspondence table for small and capital Unicode letters in the range of
+ * 0x0000 - 0xffff based on http://www.unicode.org/Public/UCA/11.0.0/allkeys.txt
+ */
+#define UNICODE_CAPITALIZATION_TABLE { \
+ { 0x0531, /* ARMENIAN CAPITAL LETTER AYB */ \
+ 0x0561, /* ARMENIAN SMALL LETTER AYB */ }, \
+ { 0x0532, /* ARMENIAN CAPITAL LETTER BEN */ \
+ 0x0562, /* ARMENIAN SMALL LETTER BEN */ }, \
+ { 0x053E, /* ARMENIAN CAPITAL LETTER CA */ \
+ 0x056E, /* ARMENIAN SMALL LETTER CA */ }, \
+ { 0x0549, /* ARMENIAN CAPITAL LETTER CHA */ \
+ 0x0579, /* ARMENIAN SMALL LETTER CHA */ }, \
+ { 0x0543, /* ARMENIAN CAPITAL LETTER CHEH */ \
+ 0x0573, /* ARMENIAN SMALL LETTER CHEH */ }, \
+ { 0x0551, /* ARMENIAN CAPITAL LETTER CO */ \
+ 0x0581, /* ARMENIAN SMALL LETTER CO */ }, \
+ { 0x0534, /* ARMENIAN CAPITAL LETTER DA */ \
+ 0x0564, /* ARMENIAN SMALL LETTER DA */ }, \
+ { 0x0535, /* ARMENIAN CAPITAL LETTER ECH */ \
+ 0x0565, /* ARMENIAN SMALL LETTER ECH */ }, \
+ { 0x0537, /* ARMENIAN CAPITAL LETTER EH */ \
+ 0x0567, /* ARMENIAN SMALL LETTER EH */ }, \
+ { 0x0538, /* ARMENIAN CAPITAL LETTER ET */ \
+ 0x0568, /* ARMENIAN SMALL LETTER ET */ }, \
+ { 0x0556, /* ARMENIAN CAPITAL LETTER FEH */ \
+ 0x0586, /* ARMENIAN SMALL LETTER FEH */ }, \
+ { 0x0542, /* ARMENIAN CAPITAL LETTER GHAD */ \
+ 0x0572, /* ARMENIAN SMALL LETTER GHAD */ }, \
+ { 0x0533, /* ARMENIAN CAPITAL LETTER GIM */ \
+ 0x0563, /* ARMENIAN SMALL LETTER GIM */ }, \
+ { 0x0540, /* ARMENIAN CAPITAL LETTER HO */ \
+ 0x0570, /* ARMENIAN SMALL LETTER HO */ }, \
+ { 0x053B, /* ARMENIAN CAPITAL LETTER INI */ \
+ 0x056B, /* ARMENIAN SMALL LETTER INI */ }, \
+ { 0x0541, /* ARMENIAN CAPITAL LETTER JA */ \
+ 0x0571, /* ARMENIAN SMALL LETTER JA */ }, \
+ { 0x054B, /* ARMENIAN CAPITAL LETTER JHEH */ \
+ 0x057B, /* ARMENIAN SMALL LETTER JHEH */ }, \
+ { 0x0554, /* ARMENIAN CAPITAL LETTER KEH */ \
+ 0x0584, /* ARMENIAN SMALL LETTER KEH */ }, \
+ { 0x053F, /* ARMENIAN CAPITAL LETTER KEN */ \
+ 0x056F, /* ARMENIAN SMALL LETTER KEN */ }, \
+ { 0x053C, /* ARMENIAN CAPITAL LETTER LIWN */ \
+ 0x056C, /* ARMENIAN SMALL LETTER LIWN */ }, \
+ { 0x0544, /* ARMENIAN CAPITAL LETTER MEN */ \
+ 0x0574, /* ARMENIAN SMALL LETTER MEN */ }, \
+ { 0x0546, /* ARMENIAN CAPITAL LETTER NOW */ \
+ 0x0576, /* ARMENIAN SMALL LETTER NOW */ }, \
+ { 0x0555, /* ARMENIAN CAPITAL LETTER OH */ \
+ 0x0585, /* ARMENIAN SMALL LETTER OH */ }, \
+ { 0x054A, /* ARMENIAN CAPITAL LETTER PEH */ \
+ 0x057A, /* ARMENIAN SMALL LETTER PEH */ }, \
+ { 0x0553, /* ARMENIAN CAPITAL LETTER PIWR */ \
+ 0x0583, /* ARMENIAN SMALL LETTER PIWR */ }, \
+ { 0x054C, /* ARMENIAN CAPITAL LETTER RA */ \
+ 0x057C, /* ARMENIAN SMALL LETTER RA */ }, \
+ { 0x0550, /* ARMENIAN CAPITAL LETTER REH */ \
+ 0x0580, /* ARMENIAN SMALL LETTER REH */ }, \
+ { 0x054D, /* ARMENIAN CAPITAL LETTER SEH */ \
+ 0x057D, /* ARMENIAN SMALL LETTER SEH */ }, \
+ { 0x0547, /* ARMENIAN CAPITAL LETTER SHA */ \
+ 0x0577, /* ARMENIAN SMALL LETTER SHA */ }, \
+ { 0x054F, /* ARMENIAN CAPITAL LETTER TIWN */ \
+ 0x057F, /* ARMENIAN SMALL LETTER TIWN */ }, \
+ { 0x0539, /* ARMENIAN CAPITAL LETTER TO */ \
+ 0x0569, /* ARMENIAN SMALL LETTER TO */ }, \
+ { 0x054E, /* ARMENIAN CAPITAL LETTER VEW */ \
+ 0x057E, /* ARMENIAN SMALL LETTER VEW */ }, \
+ { 0x0548, /* ARMENIAN CAPITAL LETTER VO */ \
+ 0x0578, /* ARMENIAN SMALL LETTER VO */ }, \
+ { 0x053D, /* ARMENIAN CAPITAL LETTER XEH */ \
+ 0x056D, /* ARMENIAN SMALL LETTER XEH */ }, \
+ { 0x0545, /* ARMENIAN CAPITAL LETTER YI */ \
+ 0x0575, /* ARMENIAN SMALL LETTER YI */ }, \
+ { 0x0552, /* ARMENIAN CAPITAL LETTER YIWN */ \
+ 0x0582, /* ARMENIAN SMALL LETTER YIWN */ }, \
+ { 0x0536, /* ARMENIAN CAPITAL LETTER ZA */ \
+ 0x0566, /* ARMENIAN SMALL LETTER ZA */ }, \
+ { 0x053A, /* ARMENIAN CAPITAL LETTER ZHE */ \
+ 0x056A, /* ARMENIAN SMALL LETTER ZHE */ }, \
+ { 0x24B6, /* CIRCLED LATIN CAPITAL LETTER A */ \
+ 0x24D0, /* CIRCLED LATIN SMALL LETTER A */ }, \
+ { 0x24B7, /* CIRCLED LATIN CAPITAL LETTER B */ \
+ 0x24D1, /* CIRCLED LATIN SMALL LETTER B */ }, \
+ { 0x24B8, /* CIRCLED LATIN CAPITAL LETTER C */ \
+ 0x24D2, /* CIRCLED LATIN SMALL LETTER C */ }, \
+ { 0x24B9, /* CIRCLED LATIN CAPITAL LETTER D */ \
+ 0x24D3, /* CIRCLED LATIN SMALL LETTER D */ }, \
+ { 0x24BA, /* CIRCLED LATIN CAPITAL LETTER E */ \
+ 0x24D4, /* CIRCLED LATIN SMALL LETTER E */ }, \
+ { 0x24BB, /* CIRCLED LATIN CAPITAL LETTER F */ \
+ 0x24D5, /* CIRCLED LATIN SMALL LETTER F */ }, \
+ { 0x24BC, /* CIRCLED LATIN CAPITAL LETTER G */ \
+ 0x24D6, /* CIRCLED LATIN SMALL LETTER G */ }, \
+ { 0x24BD, /* CIRCLED LATIN CAPITAL LETTER H */ \
+ 0x24D7, /* CIRCLED LATIN SMALL LETTER H */ }, \
+ { 0x24BE, /* CIRCLED LATIN CAPITAL LETTER I */ \
+ 0x24D8, /* CIRCLED LATIN SMALL LETTER I */ }, \
+ { 0x24BF, /* CIRCLED LATIN CAPITAL LETTER J */ \
+ 0x24D9, /* CIRCLED LATIN SMALL LETTER J */ }, \
+ { 0x24C0, /* CIRCLED LATIN CAPITAL LETTER K */ \
+ 0x24DA, /* CIRCLED LATIN SMALL LETTER K */ }, \
+ { 0x24C1, /* CIRCLED LATIN CAPITAL LETTER L */ \
+ 0x24DB, /* CIRCLED LATIN SMALL LETTER L */ }, \
+ { 0x24C2, /* CIRCLED LATIN CAPITAL LETTER M */ \
+ 0x24DC, /* CIRCLED LATIN SMALL LETTER M */ }, \
+ { 0x24C3, /* CIRCLED LATIN CAPITAL LETTER N */ \
+ 0x24DD, /* CIRCLED LATIN SMALL LETTER N */ }, \
+ { 0x24C4, /* CIRCLED LATIN CAPITAL LETTER O */ \
+ 0x24DE, /* CIRCLED LATIN SMALL LETTER O */ }, \
+ { 0x24C5, /* CIRCLED LATIN CAPITAL LETTER P */ \
+ 0x24DF, /* CIRCLED LATIN SMALL LETTER P */ }, \
+ { 0x24C6, /* CIRCLED LATIN CAPITAL LETTER Q */ \
+ 0x24E0, /* CIRCLED LATIN SMALL LETTER Q */ }, \
+ { 0x24C7, /* CIRCLED LATIN CAPITAL LETTER R */ \
+ 0x24E1, /* CIRCLED LATIN SMALL LETTER R */ }, \
+ { 0x24C8, /* CIRCLED LATIN CAPITAL LETTER S */ \
+ 0x24E2, /* CIRCLED LATIN SMALL LETTER S */ }, \
+ { 0x24C9, /* CIRCLED LATIN CAPITAL LETTER T */ \
+ 0x24E3, /* CIRCLED LATIN SMALL LETTER T */ }, \
+ { 0x24CA, /* CIRCLED LATIN CAPITAL LETTER U */ \
+ 0x24E4, /* CIRCLED LATIN SMALL LETTER U */ }, \
+ { 0x24CB, /* CIRCLED LATIN CAPITAL LETTER V */ \
+ 0x24E5, /* CIRCLED LATIN SMALL LETTER V */ }, \
+ { 0x24CC, /* CIRCLED LATIN CAPITAL LETTER W */ \
+ 0x24E6, /* CIRCLED LATIN SMALL LETTER W */ }, \
+ { 0x24CD, /* CIRCLED LATIN CAPITAL LETTER X */ \
+ 0x24E7, /* CIRCLED LATIN SMALL LETTER X */ }, \
+ { 0x24CE, /* CIRCLED LATIN CAPITAL LETTER Y */ \
+ 0x24E8, /* CIRCLED LATIN SMALL LETTER Y */ }, \
+ { 0x24CF, /* CIRCLED LATIN CAPITAL LETTER Z */ \
+ 0x24E9, /* CIRCLED LATIN SMALL LETTER Z */ }, \
+ { 0x2CC8, /* COPTIC CAPITAL LETTER AKHMIMIC KHEI */ \
+ 0x2CC9, /* COPTIC SMALL LETTER AKHMIMIC KHEI */ }, \
+ { 0x2C80, /* COPTIC CAPITAL LETTER ALFA */ \
+ 0x2C81, /* COPTIC SMALL LETTER ALFA */ }, \
+ { 0x2CF2, /* COPTIC CAPITAL LETTER BOHAIRIC KHEI */ \
+ 0x2CF3, /* COPTIC SMALL LETTER BOHAIRIC KHEI */ }, \
+ { 0x2CC2, /* COPTIC CAPITAL LETTER CROSSED SHEI */ \
+ 0x2CC3, /* COPTIC SMALL LETTER CROSSED SHEI */ }, \
+ { 0x2CB6, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ \
+ 0x2CB7, /* COPTIC SMALL LETTER CRYPTOGRAMMIC EIE */ }, \
+ { 0x2CED, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA */ \
+ 0x2CEE, /* COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA */ }, \
+ { 0x2CBC, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ \
+ 0x2CBD, /* COPTIC SMALL LETTER CRYPTOGRAMMIC NI */ }, \
+ { 0x2CEB, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI */ \
+ 0x2CEC, /* COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI */ }, \
+ { 0x2C86, /* COPTIC CAPITAL LETTER DALDA */ \
+ 0x2C87, /* COPTIC SMALL LETTER DALDA */ }, \
+ { 0x03EE, /* COPTIC CAPITAL LETTER DEI */ \
+ 0x03EF, /* COPTIC SMALL LETTER DEI */ }, \
+ { 0x2CB2, /* COPTIC CAPITAL LETTER DIALECT-P ALEF */ \
+ 0x2CB3, /* COPTIC SMALL LETTER DIALECT-P ALEF */ }, \
+ { 0x2CCA, /* COPTIC CAPITAL LETTER DIALECT-P HORI */ \
+ 0x2CCB, /* COPTIC SMALL LETTER DIALECT-P HORI */ }, \
+ { 0x2CB8, /* COPTIC CAPITAL LETTER DIALECT-P KAPA */ \
+ 0x2CB9, /* COPTIC SMALL LETTER DIALECT-P KAPA */ }, \
+ { 0x2CBA, /* COPTIC CAPITAL LETTER DIALECT-P NI */ \
+ 0x2CBB, /* COPTIC SMALL LETTER DIALECT-P NI */ }, \
+ { 0x2C88, /* COPTIC CAPITAL LETTER EIE */ \
+ 0x2C89, /* COPTIC SMALL LETTER EIE */ }, \
+ { 0x03E4, /* COPTIC CAPITAL LETTER FEI */ \
+ 0x03E5, /* COPTIC SMALL LETTER FEI */ }, \
+ { 0x2CAA, /* COPTIC CAPITAL LETTER FI */ \
+ 0x2CAB, /* COPTIC SMALL LETTER FI */ }, \
+ { 0x2C84, /* COPTIC CAPITAL LETTER GAMMA */ \
+ 0x2C85, /* COPTIC SMALL LETTER GAMMA */ }, \
+ { 0x03EA, /* COPTIC CAPITAL LETTER GANGIA */ \
+ 0x03EB, /* COPTIC SMALL LETTER GANGIA */ }, \
+ { 0x2C8E, /* COPTIC CAPITAL LETTER HATE */ \
+ 0x2C8F, /* COPTIC SMALL LETTER HATE */ }, \
+ { 0x03E8, /* COPTIC CAPITAL LETTER HORI */ \
+ 0x03E9, /* COPTIC SMALL LETTER HORI */ }, \
+ { 0x2C92, /* COPTIC CAPITAL LETTER IAUDA */ \
+ 0x2C93, /* COPTIC SMALL LETTER IAUDA */ }, \
+ { 0x2C94, /* COPTIC CAPITAL LETTER KAPA */ \
+ 0x2C95, /* COPTIC SMALL LETTER KAPA */ }, \
+ { 0x03E6, /* COPTIC CAPITAL LETTER KHEI */ \
+ 0x03E7, /* COPTIC SMALL LETTER KHEI */ }, \
+ { 0x2CAC, /* COPTIC CAPITAL LETTER KHI */ \
+ 0x2CAD, /* COPTIC SMALL LETTER KHI */ }, \
+ { 0x2C9C, /* COPTIC CAPITAL LETTER KSI */ \
+ 0x2C9D, /* COPTIC SMALL LETTER KSI */ }, \
+ { 0x2C96, /* COPTIC CAPITAL LETTER LAULA */ \
+ 0x2C97, /* COPTIC SMALL LETTER LAULA */ }, \
+ { 0x2CD0, /* COPTIC CAPITAL LETTER L-SHAPED HA */ \
+ 0x2CD1, /* COPTIC SMALL LETTER L-SHAPED HA */ }, \
+ { 0x2C98, /* COPTIC CAPITAL LETTER MI */ \
+ 0x2C99, /* COPTIC SMALL LETTER MI */ }, \
+ { 0x2C9A, /* COPTIC CAPITAL LETTER NI */ \
+ 0x2C9B, /* COPTIC SMALL LETTER NI */ }, \
+ { 0x2C9E, /* COPTIC CAPITAL LETTER O */ \
+ 0x2C9F, /* COPTIC SMALL LETTER O */ }, \
+ { 0x2CB4, /* COPTIC CAPITAL LETTER OLD COPTIC AIN */ \
+ 0x2CB5, /* COPTIC SMALL LETTER OLD COPTIC AIN */ }, \
+ { 0x2CD8, /* COPTIC CAPITAL LETTER OLD COPTIC DJA */ \
+ 0x2CD9, /* COPTIC SMALL LETTER OLD COPTIC DJA */ }, \
+ { 0x2CC6, /* COPTIC CAPITAL LETTER OLD COPTIC ESH */ \
+ 0x2CC7, /* COPTIC SMALL LETTER OLD COPTIC ESH */ }, \
+ { 0x2CD6, /* COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ \
+ 0x2CD7, /* COPTIC SMALL LETTER OLD COPTIC GANGIA */ }, \
+ { 0x2CCE, /* COPTIC CAPITAL LETTER OLD COPTIC HA */ \
+ 0x2CCF, /* COPTIC SMALL LETTER OLD COPTIC HA */ }, \
+ { 0x2CD4, /* COPTIC CAPITAL LETTER OLD COPTIC HAT */ \
+ 0x2CD5, /* COPTIC SMALL LETTER OLD COPTIC HAT */ }, \
+ { 0x2CD2, /* COPTIC CAPITAL LETTER OLD COPTIC HEI */ \
+ 0x2CD3, /* COPTIC SMALL LETTER OLD COPTIC HEI */ }, \
+ { 0x2CCC, /* COPTIC CAPITAL LETTER OLD COPTIC HORI */ \
+ 0x2CCD, /* COPTIC SMALL LETTER OLD COPTIC HORI */ }, \
+ { 0x2CBE, /* COPTIC CAPITAL LETTER OLD COPTIC OOU */ \
+ 0x2CBF, /* COPTIC SMALL LETTER OLD COPTIC OOU */ }, \
+ { 0x2CC4, /* COPTIC CAPITAL LETTER OLD COPTIC SHEI */ \
+ 0x2CC5, /* COPTIC SMALL LETTER OLD COPTIC SHEI */ }, \
+ { 0x2CDA, /* COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ \
+ 0x2CDB, /* COPTIC SMALL LETTER OLD COPTIC SHIMA */ }, \
+ { 0x2CDE, /* COPTIC CAPITAL LETTER OLD NUBIAN NGI */ \
+ 0x2CDF, /* COPTIC SMALL LETTER OLD NUBIAN NGI */ }, \
+ { 0x2CE0, /* COPTIC CAPITAL LETTER OLD NUBIAN NYI */ \
+ 0x2CE1, /* COPTIC SMALL LETTER OLD NUBIAN NYI */ }, \
+ { 0x2CDC, /* COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ \
+ 0x2CDD, /* COPTIC SMALL LETTER OLD NUBIAN SHIMA */ }, \
+ { 0x2CE2, /* COPTIC CAPITAL LETTER OLD NUBIAN WAU */ \
+ 0x2CE3, /* COPTIC SMALL LETTER OLD NUBIAN WAU */ }, \
+ { 0x2CB0, /* COPTIC CAPITAL LETTER OOU */ \
+ 0x2CB1, /* COPTIC SMALL LETTER OOU */ }, \
+ { 0x2CA0, /* COPTIC CAPITAL LETTER PI */ \
+ 0x2CA1, /* COPTIC SMALL LETTER PI */ }, \
+ { 0x2CAE, /* COPTIC CAPITAL LETTER PSI */ \
+ 0x2CAF, /* COPTIC SMALL LETTER PSI */ }, \
+ { 0x2CA2, /* COPTIC CAPITAL LETTER RO */ \
+ 0x2CA3, /* COPTIC SMALL LETTER RO */ }, \
+ { 0x2CC0, /* COPTIC CAPITAL LETTER SAMPI */ \
+ 0x2CC1, /* COPTIC SMALL LETTER SAMPI */ }, \
+ { 0x03E2, /* COPTIC CAPITAL LETTER SHEI */ \
+ 0x03E3, /* COPTIC SMALL LETTER SHEI */ }, \
+ { 0x03EC, /* COPTIC CAPITAL LETTER SHIMA */ \
+ 0x03ED, /* COPTIC SMALL LETTER SHIMA */ }, \
+ { 0x2CA4, /* COPTIC CAPITAL LETTER SIMA */ \
+ 0x2CA5, /* COPTIC SMALL LETTER SIMA */ }, \
+ { 0x2C8A, /* COPTIC CAPITAL LETTER SOU */ \
+ 0x2C8B, /* COPTIC SMALL LETTER SOU */ }, \
+ { 0x2CA6, /* COPTIC CAPITAL LETTER TAU */ \
+ 0x2CA7, /* COPTIC SMALL LETTER TAU */ }, \
+ { 0x2C90, /* COPTIC CAPITAL LETTER THETHE */ \
+ 0x2C91, /* COPTIC SMALL LETTER THETHE */ }, \
+ { 0x2CA8, /* COPTIC CAPITAL LETTER UA */ \
+ 0x2CA9, /* COPTIC SMALL LETTER UA */ }, \
+ { 0x2C82, /* COPTIC CAPITAL LETTER VIDA */ \
+ 0x2C83, /* COPTIC SMALL LETTER VIDA */ }, \
+ { 0x2C8C, /* COPTIC CAPITAL LETTER ZATA */ \
+ 0x2C8D, /* COPTIC SMALL LETTER ZATA */ }, \
+ { 0x0410, /* CYRILLIC CAPITAL LETTER A */ \
+ 0x0430, /* CYRILLIC SMALL LETTER A */ }, \
+ { 0x04D0, /* CYRILLIC CAPITAL LETTER A WITH BREVE */ \
+ 0x04D1, /* CYRILLIC SMALL LETTER A WITH BREVE */ }, \
+ { 0x04D2, /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x04D3, /* CYRILLIC SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x04BC, /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ \
+ 0x04BD, /* CYRILLIC SMALL LETTER ABKHASIAN CHE */ }, \
+ { 0x04BE, /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */ \
+ 0x04BF, /* CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER */ }, \
+ { 0x04E0, /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ \
+ 0x04E1, /* CYRILLIC SMALL LETTER ABKHASIAN DZE */ }, \
+ { 0x04A8, /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */ \
+ 0x04A9, /* CYRILLIC SMALL LETTER ABKHASIAN HA */ }, \
+ { 0x051E, /* CYRILLIC CAPITAL LETTER ALEUT KA */ \
+ 0x051F, /* CYRILLIC SMALL LETTER ALEUT KA */ }, \
+ { 0x04E8, /* CYRILLIC CAPITAL LETTER BARRED O */ \
+ 0x04E9, /* CYRILLIC SMALL LETTER BARRED O */ }, \
+ { 0x04EA, /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */ \
+ 0x04EB, /* CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS */ }, \
+ { 0x04A0, /* CYRILLIC CAPITAL LETTER BASHKIR KA */ \
+ 0x04A1, /* CYRILLIC SMALL LETTER BASHKIR KA */ }, \
+ { 0x0411, /* CYRILLIC CAPITAL LETTER BE */ \
+ 0x0431, /* CYRILLIC SMALL LETTER BE */ }, \
+ { 0x046A, /* CYRILLIC CAPITAL LETTER BIG YUS */ \
+ 0x046B, /* CYRILLIC SMALL LETTER BIG YUS */ }, \
+ { 0xA66A, /* CYRILLIC CAPITAL LETTER BINOCULAR O */ \
+ 0xA66B, /* CYRILLIC SMALL LETTER BINOCULAR O */ }, \
+ { 0xA65A, /* CYRILLIC CAPITAL LETTER BLENDED YUS */ \
+ 0xA65B, /* CYRILLIC SMALL LETTER BLENDED YUS */ }, \
+ { 0xA64C, /* CYRILLIC CAPITAL LETTER BROAD OMEGA */ \
+ 0xA64D, /* CYRILLIC SMALL LETTER BROAD OMEGA */ }, \
+ { 0x0406, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ \
+ 0x0456, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ }, \
+ { 0xA686, /* CYRILLIC CAPITAL LETTER CCHE */ \
+ 0xA687, /* CYRILLIC SMALL LETTER CCHE */ }, \
+ { 0x0427, /* CYRILLIC CAPITAL LETTER CHE */ \
+ 0x0447, /* CYRILLIC SMALL LETTER CHE */ }, \
+ { 0x04B6, /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */ \
+ 0x04B7, /* CYRILLIC SMALL LETTER CHE WITH DESCENDER */ }, \
+ { 0x04F4, /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */ \
+ 0x04F5, /* CYRILLIC SMALL LETTER CHE WITH DIAERESIS */ }, \
+ { 0x04B8, /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */ \
+ 0x04B9, /* CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE */ }, \
+ { 0xA658, /* CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS */ \
+ 0xA659, /* CYRILLIC SMALL LETTER CLOSED LITTLE YUS */ }, \
+ { 0xA69A, /* CYRILLIC CAPITAL LETTER CROSSED O */ \
+ 0xA69B, /* CYRILLIC SMALL LETTER CROSSED O */ }, \
+ { 0x052C, /* CYRILLIC CAPITAL LETTER DCHE */ \
+ 0x052D, /* CYRILLIC SMALL LETTER DCHE */ }, \
+ { 0x0414, /* CYRILLIC CAPITAL LETTER DE */ \
+ 0x0434, /* CYRILLIC SMALL LETTER DE */ }, \
+ { 0x0402, /* CYRILLIC CAPITAL LETTER DJE */ \
+ 0x0452, /* CYRILLIC SMALL LETTER DJE */ }, \
+ { 0xA648, /* CYRILLIC CAPITAL LETTER DJERV */ \
+ 0xA649, /* CYRILLIC SMALL LETTER DJERV */ }, \
+ { 0xA66C, /* CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O */ \
+ 0xA66D, /* CYRILLIC SMALL LETTER DOUBLE MONOCULAR O */ }, \
+ { 0xA698, /* CYRILLIC CAPITAL LETTER DOUBLE O */ \
+ 0xA699, /* CYRILLIC SMALL LETTER DOUBLE O */ }, \
+ { 0xA680, /* CYRILLIC CAPITAL LETTER DWE */ \
+ 0xA681, /* CYRILLIC SMALL LETTER DWE */ }, \
+ { 0x0405, /* CYRILLIC CAPITAL LETTER DZE */ \
+ 0x0455, /* CYRILLIC SMALL LETTER DZE */ }, \
+ { 0xA642, /* CYRILLIC CAPITAL LETTER DZELO */ \
+ 0xA643, /* CYRILLIC SMALL LETTER DZELO */ }, \
+ { 0x040F, /* CYRILLIC CAPITAL LETTER DZHE */ \
+ 0x045F, /* CYRILLIC SMALL LETTER DZHE */ }, \
+ { 0xA682, /* CYRILLIC CAPITAL LETTER DZWE */ \
+ 0xA683, /* CYRILLIC SMALL LETTER DZWE */ }, \
+ { 0xA688, /* CYRILLIC CAPITAL LETTER DZZE */ \
+ 0xA689, /* CYRILLIC SMALL LETTER DZZE */ }, \
+ { 0x052A, /* CYRILLIC CAPITAL LETTER DZZHE */ \
+ 0x052B, /* CYRILLIC SMALL LETTER DZZHE */ }, \
+ { 0x042D, /* CYRILLIC CAPITAL LETTER E */ \
+ 0x044D, /* CYRILLIC SMALL LETTER E */ }, \
+ { 0x04EC, /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS */ \
+ 0x04ED, /* CYRILLIC SMALL LETTER E WITH DIAERESIS */ }, \
+ { 0x0424, /* CYRILLIC CAPITAL LETTER EF */ \
+ 0x0444, /* CYRILLIC SMALL LETTER EF */ }, \
+ { 0x041B, /* CYRILLIC CAPITAL LETTER EL */ \
+ 0x043B, /* CYRILLIC SMALL LETTER EL */ }, \
+ { 0x052E, /* CYRILLIC CAPITAL LETTER EL WITH DESCENDER */ \
+ 0x052F, /* CYRILLIC SMALL LETTER EL WITH DESCENDER */ }, \
+ { 0x0512, /* CYRILLIC CAPITAL LETTER EL WITH HOOK */ \
+ 0x0513, /* CYRILLIC SMALL LETTER EL WITH HOOK */ }, \
+ { 0x0520, /* CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK */ \
+ 0x0521, /* CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK */ }, \
+ { 0x04C5, /* CYRILLIC CAPITAL LETTER EL WITH TAIL */ \
+ 0x04C6, /* CYRILLIC SMALL LETTER EL WITH TAIL */ }, \
+ { 0x041C, /* CYRILLIC CAPITAL LETTER EM */ \
+ 0x043C, /* CYRILLIC SMALL LETTER EM */ }, \
+ { 0x04CD, /* CYRILLIC CAPITAL LETTER EM WITH TAIL */ \
+ 0x04CE, /* CYRILLIC SMALL LETTER EM WITH TAIL */ }, \
+ { 0x041D, /* CYRILLIC CAPITAL LETTER EN */ \
+ 0x043D, /* CYRILLIC SMALL LETTER EN */ }, \
+ { 0x04A2, /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */ \
+ 0x04A3, /* CYRILLIC SMALL LETTER EN WITH DESCENDER */ }, \
+ { 0x04C7, /* CYRILLIC CAPITAL LETTER EN WITH HOOK */ \
+ 0x04C8, /* CYRILLIC SMALL LETTER EN WITH HOOK */ }, \
+ { 0x0528, /* CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK */ \
+ 0x0529, /* CYRILLIC SMALL LETTER EN WITH LEFT HOOK */ }, \
+ { 0x0522, /* CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK */ \
+ 0x0523, /* CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK */ }, \
+ { 0x04C9, /* CYRILLIC CAPITAL LETTER EN WITH TAIL */ \
+ 0x04CA, /* CYRILLIC SMALL LETTER EN WITH TAIL */ }, \
+ { 0x0420, /* CYRILLIC CAPITAL LETTER ER */ \
+ 0x0440, /* CYRILLIC SMALL LETTER ER */ }, \
+ { 0x048E, /* CYRILLIC CAPITAL LETTER ER WITH TICK */ \
+ 0x048F, /* CYRILLIC SMALL LETTER ER WITH TICK */ }, \
+ { 0x0421, /* CYRILLIC CAPITAL LETTER ES */ \
+ 0x0441, /* CYRILLIC SMALL LETTER ES */ }, \
+ { 0x04AA, /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */ \
+ 0x04AB, /* CYRILLIC SMALL LETTER ES WITH DESCENDER */ }, \
+ { 0x0472, /* CYRILLIC CAPITAL LETTER FITA */ \
+ 0x0473, /* CYRILLIC SMALL LETTER FITA */ }, \
+ { 0x0413, /* CYRILLIC CAPITAL LETTER GHE */ \
+ 0x0433, /* CYRILLIC SMALL LETTER GHE */ }, \
+ { 0x04F6, /* CYRILLIC CAPITAL LETTER GHE WITH DESCENDER */ \
+ 0x04F7, /* CYRILLIC SMALL LETTER GHE WITH DESCENDER */ }, \
+ { 0x0494, /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */ \
+ 0x0495, /* CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK */ }, \
+ { 0x0492, /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */ \
+ 0x0493, /* CYRILLIC SMALL LETTER GHE WITH STROKE */ }, \
+ { 0x04FA, /* CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK */ \
+ 0x04FB, /* CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK */ }, \
+ { 0x0490, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ \
+ 0x0491, /* CYRILLIC SMALL LETTER GHE WITH UPTURN */ }, \
+ { 0x0403, /* CYRILLIC CAPITAL LETTER GJE */ \
+ 0x0453, /* CYRILLIC SMALL LETTER GJE */ }, \
+ { 0x0425, /* CYRILLIC CAPITAL LETTER HA */ \
+ 0x0445, /* CYRILLIC SMALL LETTER HA */ }, \
+ { 0x04B2, /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */ \
+ 0x04B3, /* CYRILLIC SMALL LETTER HA WITH DESCENDER */ }, \
+ { 0x04FC, /* CYRILLIC CAPITAL LETTER HA WITH HOOK */ \
+ 0x04FD, /* CYRILLIC SMALL LETTER HA WITH HOOK */ }, \
+ { 0x04FE, /* CYRILLIC CAPITAL LETTER HA WITH STROKE */ \
+ 0x04FF, /* CYRILLIC SMALL LETTER HA WITH STROKE */ }, \
+ { 0x042A, /* CYRILLIC CAPITAL LETTER HARD SIGN */ \
+ 0x044A, /* CYRILLIC SMALL LETTER HARD SIGN */ }, \
+ { 0xA694, /* CYRILLIC CAPITAL LETTER HWE */ \
+ 0xA695, /* CYRILLIC SMALL LETTER HWE */ }, \
+ { 0x0418, /* CYRILLIC CAPITAL LETTER I */ \
+ 0x0438, /* CYRILLIC SMALL LETTER I */ }, \
+ { 0x04E4, /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */ \
+ 0x04E5, /* CYRILLIC SMALL LETTER I WITH DIAERESIS */ }, \
+ { 0x040D, /* CYRILLIC CAPITAL LETTER I WITH GRAVE */ \
+ 0x045D, /* CYRILLIC SMALL LETTER I WITH GRAVE */ }, \
+ { 0x04E2, /* CYRILLIC CAPITAL LETTER I WITH MACRON */ \
+ 0x04E3, /* CYRILLIC SMALL LETTER I WITH MACRON */ }, \
+ { 0x0415, /* CYRILLIC CAPITAL LETTER IE */ \
+ 0x0435, /* CYRILLIC SMALL LETTER IE */ }, \
+ { 0x04D6, /* CYRILLIC CAPITAL LETTER IE WITH BREVE */ \
+ 0x04D7, /* CYRILLIC SMALL LETTER IE WITH BREVE */ }, \
+ { 0x0400, /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */ \
+ 0x0450, /* CYRILLIC SMALL LETTER IE WITH GRAVE */ }, \
+ { 0x0401, /* CYRILLIC CAPITAL LETTER IO */ \
+ 0x0451, /* CYRILLIC SMALL LETTER IO */ }, \
+ { 0xA646, /* CYRILLIC CAPITAL LETTER IOTA */ \
+ 0xA647, /* CYRILLIC SMALL LETTER IOTA */ }, \
+ { 0xA656, /* CYRILLIC CAPITAL LETTER IOTIFIED A */ \
+ 0xA657, /* CYRILLIC SMALL LETTER IOTIFIED A */ }, \
+ { 0x046C, /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */ \
+ 0x046D, /* CYRILLIC SMALL LETTER IOTIFIED BIG YUS */ }, \
+ { 0xA65C, /* CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS */ \
+ 0xA65D, /* CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS */ }, \
+ { 0x0464, /* CYRILLIC CAPITAL LETTER IOTIFIED E */ \
+ 0x0465, /* CYRILLIC SMALL LETTER IOTIFIED E */ }, \
+ { 0x0468, /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */ \
+ 0x0469, /* CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS */ }, \
+ { 0xA652, /* CYRILLIC CAPITAL LETTER IOTIFIED YAT */ \
+ 0xA653, /* CYRILLIC SMALL LETTER IOTIFIED YAT */ }, \
+ { 0x0474, /* CYRILLIC CAPITAL LETTER IZHITSA */ \
+ 0x0475, /* CYRILLIC SMALL LETTER IZHITSA */ }, \
+ { 0x0476, /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */ \
+ 0x0477, /* CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */ }, \
+ { 0x0408, /* CYRILLIC CAPITAL LETTER JE */ \
+ 0x0458, /* CYRILLIC SMALL LETTER JE */ }, \
+ { 0x041A, /* CYRILLIC CAPITAL LETTER KA */ \
+ 0x043A, /* CYRILLIC SMALL LETTER KA */ }, \
+ { 0x049A, /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */ \
+ 0x049B, /* CYRILLIC SMALL LETTER KA WITH DESCENDER */ }, \
+ { 0x04C3, /* CYRILLIC CAPITAL LETTER KA WITH HOOK */ \
+ 0x04C4, /* CYRILLIC SMALL LETTER KA WITH HOOK */ }, \
+ { 0x049E, /* CYRILLIC CAPITAL LETTER KA WITH STROKE */ \
+ 0x049F, /* CYRILLIC SMALL LETTER KA WITH STROKE */ }, \
+ { 0x049C, /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */ \
+ 0x049D, /* CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE */ }, \
+ { 0x04CB, /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */ \
+ 0x04CC, /* CYRILLIC SMALL LETTER KHAKASSIAN CHE */ }, \
+ { 0x040C, /* CYRILLIC CAPITAL LETTER KJE */ \
+ 0x045C, /* CYRILLIC SMALL LETTER KJE */ }, \
+ { 0x0500, /* CYRILLIC CAPITAL LETTER KOMI DE */ \
+ 0x0501, /* CYRILLIC SMALL LETTER KOMI DE */ }, \
+ { 0x0502, /* CYRILLIC CAPITAL LETTER KOMI DJE */ \
+ 0x0503, /* CYRILLIC SMALL LETTER KOMI DJE */ }, \
+ { 0x0506, /* CYRILLIC CAPITAL LETTER KOMI DZJE */ \
+ 0x0507, /* CYRILLIC SMALL LETTER KOMI DZJE */ }, \
+ { 0x0508, /* CYRILLIC CAPITAL LETTER KOMI LJE */ \
+ 0x0509, /* CYRILLIC SMALL LETTER KOMI LJE */ }, \
+ { 0x050A, /* CYRILLIC CAPITAL LETTER KOMI NJE */ \
+ 0x050B, /* CYRILLIC SMALL LETTER KOMI NJE */ }, \
+ { 0x050C, /* CYRILLIC CAPITAL LETTER KOMI SJE */ \
+ 0x050D, /* CYRILLIC SMALL LETTER KOMI SJE */ }, \
+ { 0x050E, /* CYRILLIC CAPITAL LETTER KOMI TJE */ \
+ 0x050F, /* CYRILLIC SMALL LETTER KOMI TJE */ }, \
+ { 0x0504, /* CYRILLIC CAPITAL LETTER KOMI ZJE */ \
+ 0x0505, /* CYRILLIC SMALL LETTER KOMI ZJE */ }, \
+ { 0x0480, /* CYRILLIC CAPITAL LETTER KOPPA */ \
+ 0x0481, /* CYRILLIC SMALL LETTER KOPPA */ }, \
+ { 0x046E, /* CYRILLIC CAPITAL LETTER KSI */ \
+ 0x046F, /* CYRILLIC SMALL LETTER KSI */ }, \
+ { 0x0514, /* CYRILLIC CAPITAL LETTER LHA */ \
+ 0x0515, /* CYRILLIC SMALL LETTER LHA */ }, \
+ { 0x0466, /* CYRILLIC CAPITAL LETTER LITTLE YUS */ \
+ 0x0467, /* CYRILLIC SMALL LETTER LITTLE YUS */ }, \
+ { 0x0409, /* CYRILLIC CAPITAL LETTER LJE */ \
+ 0x0459, /* CYRILLIC SMALL LETTER LJE */ }, \
+ { 0xA668, /* CYRILLIC CAPITAL LETTER MONOCULAR O */ \
+ 0xA669, /* CYRILLIC SMALL LETTER MONOCULAR O */ }, \
+ { 0xA64A, /* CYRILLIC CAPITAL LETTER MONOGRAPH UK */ \
+ 0xA64B, /* CYRILLIC SMALL LETTER MONOGRAPH UK */ }, \
+ { 0xA64E, /* CYRILLIC CAPITAL LETTER NEUTRAL YER */ \
+ 0xA64F, /* CYRILLIC SMALL LETTER NEUTRAL YER */ }, \
+ { 0x040A, /* CYRILLIC CAPITAL LETTER NJE */ \
+ 0x045A, /* CYRILLIC SMALL LETTER NJE */ }, \
+ { 0x041E, /* CYRILLIC CAPITAL LETTER O */ \
+ 0x043E, /* CYRILLIC SMALL LETTER O */ }, \
+ { 0x04E6, /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x04E7, /* CYRILLIC SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x0460, /* CYRILLIC CAPITAL LETTER OMEGA */ \
+ 0x0461, /* CYRILLIC SMALL LETTER OMEGA */ }, \
+ { 0x047C, /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */ \
+ 0x047D, /* CYRILLIC SMALL LETTER OMEGA WITH TITLO */ }, \
+ { 0x047E, /* CYRILLIC CAPITAL LETTER OT */ \
+ 0x047F, /* CYRILLIC SMALL LETTER OT */ }, \
+ { 0x041F, /* CYRILLIC CAPITAL LETTER PE */ \
+ 0x043F, /* CYRILLIC SMALL LETTER PE */ }, \
+ { 0x0524, /* CYRILLIC CAPITAL LETTER PE WITH DESCENDER */ \
+ 0x0525, /* CYRILLIC SMALL LETTER PE WITH DESCENDER */ }, \
+ { 0x04A6, /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */ \
+ 0x04A7, /* CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK */ }, \
+ { 0x0470, /* CYRILLIC CAPITAL LETTER PSI */ \
+ 0x0471, /* CYRILLIC SMALL LETTER PSI */ }, \
+ { 0x051A, /* CYRILLIC CAPITAL LETTER QA */ \
+ 0x051B, /* CYRILLIC SMALL LETTER QA */ }, \
+ { 0xA644, /* CYRILLIC CAPITAL LETTER REVERSED DZE */ \
+ 0xA645, /* CYRILLIC SMALL LETTER REVERSED DZE */ }, \
+ { 0xA660, /* CYRILLIC CAPITAL LETTER REVERSED TSE */ \
+ 0xA661, /* CYRILLIC SMALL LETTER REVERSED TSE */ }, \
+ { 0xA654, /* CYRILLIC CAPITAL LETTER REVERSED YU */ \
+ 0xA655, /* CYRILLIC SMALL LETTER REVERSED YU */ }, \
+ { 0x0510, /* CYRILLIC CAPITAL LETTER REVERSED ZE */ \
+ 0x0511, /* CYRILLIC SMALL LETTER REVERSED ZE */ }, \
+ { 0x0516, /* CYRILLIC CAPITAL LETTER RHA */ \
+ 0x0517, /* CYRILLIC SMALL LETTER RHA */ }, \
+ { 0x047A, /* CYRILLIC CAPITAL LETTER ROUND OMEGA */ \
+ 0x047B, /* CYRILLIC SMALL LETTER ROUND OMEGA */ }, \
+ { 0x04D8, /* CYRILLIC CAPITAL LETTER SCHWA */ \
+ 0x04D9, /* CYRILLIC SMALL LETTER SCHWA */ }, \
+ { 0x04DA, /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */ \
+ 0x04DB, /* CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS */ }, \
+ { 0x048C, /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ \
+ 0x048D, /* CYRILLIC SMALL LETTER SEMISOFT SIGN */ }, \
+ { 0x0428, /* CYRILLIC CAPITAL LETTER SHA */ \
+ 0x0448, /* CYRILLIC SMALL LETTER SHA */ }, \
+ { 0x0429, /* CYRILLIC CAPITAL LETTER SHCHA */ \
+ 0x0449, /* CYRILLIC SMALL LETTER SHCHA */ }, \
+ { 0x04BA, /* CYRILLIC CAPITAL LETTER SHHA */ \
+ 0x04BB, /* CYRILLIC SMALL LETTER SHHA */ }, \
+ { 0x0526, /* CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER */ \
+ 0x0527, /* CYRILLIC SMALL LETTER SHHA WITH DESCENDER */ }, \
+ { 0x048A, /* CYRILLIC CAPITAL LETTER SHORT I WITH TAIL */ \
+ 0x048B, /* CYRILLIC SMALL LETTER SHORT I WITH TAIL */ }, \
+ { 0x040E, /* CYRILLIC CAPITAL LETTER SHORT U */ \
+ 0x045E, /* CYRILLIC SMALL LETTER SHORT U */ }, \
+ { 0xA696, /* CYRILLIC CAPITAL LETTER SHWE */ \
+ 0xA697, /* CYRILLIC SMALL LETTER SHWE */ }, \
+ { 0xA662, /* CYRILLIC CAPITAL LETTER SOFT DE */ \
+ 0xA663, /* CYRILLIC SMALL LETTER SOFT DE */ }, \
+ { 0xA664, /* CYRILLIC CAPITAL LETTER SOFT EL */ \
+ 0xA665, /* CYRILLIC SMALL LETTER SOFT EL */ }, \
+ { 0xA666, /* CYRILLIC CAPITAL LETTER SOFT EM */ \
+ 0xA667, /* CYRILLIC SMALL LETTER SOFT EM */ }, \
+ { 0x042C, /* CYRILLIC CAPITAL LETTER SOFT SIGN */ \
+ 0x044C, /* CYRILLIC SMALL LETTER SOFT SIGN */ }, \
+ { 0x04AE, /* CYRILLIC CAPITAL LETTER STRAIGHT U */ \
+ 0x04AF, /* CYRILLIC SMALL LETTER STRAIGHT U */ }, \
+ { 0x04B0, /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */ \
+ 0x04B1, /* CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE */ }, \
+ { 0xA692, /* CYRILLIC CAPITAL LETTER TCHE */ \
+ 0xA693, /* CYRILLIC SMALL LETTER TCHE */ }, \
+ { 0x0422, /* CYRILLIC CAPITAL LETTER TE */ \
+ 0x0442, /* CYRILLIC SMALL LETTER TE */ }, \
+ { 0x04AC, /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */ \
+ 0x04AD, /* CYRILLIC SMALL LETTER TE WITH DESCENDER */ }, \
+ { 0xA68A, /* CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK */ \
+ 0xA68B, /* CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK */ }, \
+ { 0x0426, /* CYRILLIC CAPITAL LETTER TSE */ \
+ 0x0446, /* CYRILLIC SMALL LETTER TSE */ }, \
+ { 0x040B, /* CYRILLIC CAPITAL LETTER TSHE */ \
+ 0x045B, /* CYRILLIC SMALL LETTER TSHE */ }, \
+ { 0xA690, /* CYRILLIC CAPITAL LETTER TSSE */ \
+ 0xA691, /* CYRILLIC SMALL LETTER TSSE */ }, \
+ { 0xA68E, /* CYRILLIC CAPITAL LETTER TSWE */ \
+ 0xA68F, /* CYRILLIC SMALL LETTER TSWE */ }, \
+ { 0xA68C, /* CYRILLIC CAPITAL LETTER TWE */ \
+ 0xA68D, /* CYRILLIC SMALL LETTER TWE */ }, \
+ { 0x0423, /* CYRILLIC CAPITAL LETTER U */ \
+ 0x0443, /* CYRILLIC SMALL LETTER U */ }, \
+ { 0x04F0, /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x04F1, /* CYRILLIC SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x04F2, /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */ \
+ 0x04F3, /* CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE */ }, \
+ { 0x04EE, /* CYRILLIC CAPITAL LETTER U WITH MACRON */ \
+ 0x04EF, /* CYRILLIC SMALL LETTER U WITH MACRON */ }, \
+ { 0x0478, /* CYRILLIC CAPITAL LETTER UK */ \
+ 0x0479, /* CYRILLIC SMALL LETTER UK */ }, \
+ { 0x0404, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */ \
+ 0x0454, /* CYRILLIC SMALL LETTER UKRAINIAN IE */ }, \
+ { 0x0412, /* CYRILLIC CAPITAL LETTER VE */ \
+ 0x0432, /* CYRILLIC SMALL LETTER VE */ }, \
+ { 0x051C, /* CYRILLIC CAPITAL LETTER WE */ \
+ 0x051D, /* CYRILLIC SMALL LETTER WE */ }, \
+ { 0x042F, /* CYRILLIC CAPITAL LETTER YA */ \
+ 0x044F, /* CYRILLIC SMALL LETTER YA */ }, \
+ { 0x0518, /* CYRILLIC CAPITAL LETTER YAE */ \
+ 0x0519, /* CYRILLIC SMALL LETTER YAE */ }, \
+ { 0x0462, /* CYRILLIC CAPITAL LETTER YAT */ \
+ 0x0463, /* CYRILLIC SMALL LETTER YAT */ }, \
+ { 0x042B, /* CYRILLIC CAPITAL LETTER YERU */ \
+ 0x044B, /* CYRILLIC SMALL LETTER YERU */ }, \
+ { 0xA650, /* CYRILLIC CAPITAL LETTER YERU WITH BACK YER */ \
+ 0xA651, /* CYRILLIC SMALL LETTER YERU WITH BACK YER */ }, \
+ { 0x04F8, /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */ \
+ 0x04F9, /* CYRILLIC SMALL LETTER YERU WITH DIAERESIS */ }, \
+ { 0x0407, /* CYRILLIC CAPITAL LETTER YI */ \
+ 0x0457, /* CYRILLIC SMALL LETTER YI */ }, \
+ { 0xA65E, /* CYRILLIC CAPITAL LETTER YN */ \
+ 0xA65F, /* CYRILLIC SMALL LETTER YN */ }, \
+ { 0x042E, /* CYRILLIC CAPITAL LETTER YU */ \
+ 0x044E, /* CYRILLIC SMALL LETTER YU */ }, \
+ { 0x0417, /* CYRILLIC CAPITAL LETTER ZE */ \
+ 0x0437, /* CYRILLIC SMALL LETTER ZE */ }, \
+ { 0x0498, /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */ \
+ 0x0499, /* CYRILLIC SMALL LETTER ZE WITH DESCENDER */ }, \
+ { 0x04DE, /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */ \
+ 0x04DF, /* CYRILLIC SMALL LETTER ZE WITH DIAERESIS */ }, \
+ { 0xA640, /* CYRILLIC CAPITAL LETTER ZEMLYA */ \
+ 0xA641, /* CYRILLIC SMALL LETTER ZEMLYA */ }, \
+ { 0x0416, /* CYRILLIC CAPITAL LETTER ZHE */ \
+ 0x0436, /* CYRILLIC SMALL LETTER ZHE */ }, \
+ { 0x04C1, /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */ \
+ 0x04C2, /* CYRILLIC SMALL LETTER ZHE WITH BREVE */ }, \
+ { 0x0496, /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */ \
+ 0x0497, /* CYRILLIC SMALL LETTER ZHE WITH DESCENDER */ }, \
+ { 0x04DC, /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */ \
+ 0x04DD, /* CYRILLIC SMALL LETTER ZHE WITH DIAERESIS */ }, \
+ { 0xA684, /* CYRILLIC CAPITAL LETTER ZHWE */ \
+ 0xA685, /* CYRILLIC SMALL LETTER ZHWE */ }, \
+ { 0xFF21, /* FULLWIDTH LATIN CAPITAL LETTER A */ \
+ 0xFF41, /* FULLWIDTH LATIN SMALL LETTER A */ }, \
+ { 0xFF22, /* FULLWIDTH LATIN CAPITAL LETTER B */ \
+ 0xFF42, /* FULLWIDTH LATIN SMALL LETTER B */ }, \
+ { 0xFF23, /* FULLWIDTH LATIN CAPITAL LETTER C */ \
+ 0xFF43, /* FULLWIDTH LATIN SMALL LETTER C */ }, \
+ { 0xFF24, /* FULLWIDTH LATIN CAPITAL LETTER D */ \
+ 0xFF44, /* FULLWIDTH LATIN SMALL LETTER D */ }, \
+ { 0xFF25, /* FULLWIDTH LATIN CAPITAL LETTER E */ \
+ 0xFF45, /* FULLWIDTH LATIN SMALL LETTER E */ }, \
+ { 0xFF26, /* FULLWIDTH LATIN CAPITAL LETTER F */ \
+ 0xFF46, /* FULLWIDTH LATIN SMALL LETTER F */ }, \
+ { 0xFF27, /* FULLWIDTH LATIN CAPITAL LETTER G */ \
+ 0xFF47, /* FULLWIDTH LATIN SMALL LETTER G */ }, \
+ { 0xFF28, /* FULLWIDTH LATIN CAPITAL LETTER H */ \
+ 0xFF48, /* FULLWIDTH LATIN SMALL LETTER H */ }, \
+ { 0xFF29, /* FULLWIDTH LATIN CAPITAL LETTER I */ \
+ 0xFF49, /* FULLWIDTH LATIN SMALL LETTER I */ }, \
+ { 0xFF2A, /* FULLWIDTH LATIN CAPITAL LETTER J */ \
+ 0xFF4A, /* FULLWIDTH LATIN SMALL LETTER J */ }, \
+ { 0xFF2B, /* FULLWIDTH LATIN CAPITAL LETTER K */ \
+ 0xFF4B, /* FULLWIDTH LATIN SMALL LETTER K */ }, \
+ { 0xFF2C, /* FULLWIDTH LATIN CAPITAL LETTER L */ \
+ 0xFF4C, /* FULLWIDTH LATIN SMALL LETTER L */ }, \
+ { 0xFF2D, /* FULLWIDTH LATIN CAPITAL LETTER M */ \
+ 0xFF4D, /* FULLWIDTH LATIN SMALL LETTER M */ }, \
+ { 0xFF2E, /* FULLWIDTH LATIN CAPITAL LETTER N */ \
+ 0xFF4E, /* FULLWIDTH LATIN SMALL LETTER N */ }, \
+ { 0xFF2F, /* FULLWIDTH LATIN CAPITAL LETTER O */ \
+ 0xFF4F, /* FULLWIDTH LATIN SMALL LETTER O */ }, \
+ { 0xFF30, /* FULLWIDTH LATIN CAPITAL LETTER P */ \
+ 0xFF50, /* FULLWIDTH LATIN SMALL LETTER P */ }, \
+ { 0xFF31, /* FULLWIDTH LATIN CAPITAL LETTER Q */ \
+ 0xFF51, /* FULLWIDTH LATIN SMALL LETTER Q */ }, \
+ { 0xFF32, /* FULLWIDTH LATIN CAPITAL LETTER R */ \
+ 0xFF52, /* FULLWIDTH LATIN SMALL LETTER R */ }, \
+ { 0xFF33, /* FULLWIDTH LATIN CAPITAL LETTER S */ \
+ 0xFF53, /* FULLWIDTH LATIN SMALL LETTER S */ }, \
+ { 0xFF34, /* FULLWIDTH LATIN CAPITAL LETTER T */ \
+ 0xFF54, /* FULLWIDTH LATIN SMALL LETTER T */ }, \
+ { 0xFF35, /* FULLWIDTH LATIN CAPITAL LETTER U */ \
+ 0xFF55, /* FULLWIDTH LATIN SMALL LETTER U */ }, \
+ { 0xFF36, /* FULLWIDTH LATIN CAPITAL LETTER V */ \
+ 0xFF56, /* FULLWIDTH LATIN SMALL LETTER V */ }, \
+ { 0xFF37, /* FULLWIDTH LATIN CAPITAL LETTER W */ \
+ 0xFF57, /* FULLWIDTH LATIN SMALL LETTER W */ }, \
+ { 0xFF38, /* FULLWIDTH LATIN CAPITAL LETTER X */ \
+ 0xFF58, /* FULLWIDTH LATIN SMALL LETTER X */ }, \
+ { 0xFF39, /* FULLWIDTH LATIN CAPITAL LETTER Y */ \
+ 0xFF59, /* FULLWIDTH LATIN SMALL LETTER Y */ }, \
+ { 0xFF3A, /* FULLWIDTH LATIN CAPITAL LETTER Z */ \
+ 0xFF5A, /* FULLWIDTH LATIN SMALL LETTER Z */ }, \
+ { 0x10CD, /* GEORGIAN CAPITAL LETTER AEN */ \
+ 0x2D2D, /* GEORGIAN SMALL LETTER AEN */ }, \
+ { 0x10A0, /* GEORGIAN CAPITAL LETTER AN */ \
+ 0x2D00, /* GEORGIAN SMALL LETTER AN */ }, \
+ { 0x10A1, /* GEORGIAN CAPITAL LETTER BAN */ \
+ 0x2D01, /* GEORGIAN SMALL LETTER BAN */ }, \
+ { 0x10BA, /* GEORGIAN CAPITAL LETTER CAN */ \
+ 0x2D1A, /* GEORGIAN SMALL LETTER CAN */ }, \
+ { 0x10BD, /* GEORGIAN CAPITAL LETTER CHAR */ \
+ 0x2D1D, /* GEORGIAN SMALL LETTER CHAR */ }, \
+ { 0x10B9, /* GEORGIAN CAPITAL LETTER CHIN */ \
+ 0x2D19, /* GEORGIAN SMALL LETTER CHIN */ }, \
+ { 0x10BC, /* GEORGIAN CAPITAL LETTER CIL */ \
+ 0x2D1C, /* GEORGIAN SMALL LETTER CIL */ }, \
+ { 0x10A3, /* GEORGIAN CAPITAL LETTER DON */ \
+ 0x2D03, /* GEORGIAN SMALL LETTER DON */ }, \
+ { 0x10A4, /* GEORGIAN CAPITAL LETTER EN */ \
+ 0x2D04, /* GEORGIAN SMALL LETTER EN */ }, \
+ { 0x10A2, /* GEORGIAN CAPITAL LETTER GAN */ \
+ 0x2D02, /* GEORGIAN SMALL LETTER GAN */ }, \
+ { 0x10B6, /* GEORGIAN CAPITAL LETTER GHAN */ \
+ 0x2D16, /* GEORGIAN SMALL LETTER GHAN */ }, \
+ { 0x10C0, /* GEORGIAN CAPITAL LETTER HAE */ \
+ 0x2D20, /* GEORGIAN SMALL LETTER HAE */ }, \
+ { 0x10C4, /* GEORGIAN CAPITAL LETTER HAR */ \
+ 0x2D24, /* GEORGIAN SMALL LETTER HAR */ }, \
+ { 0x10C1, /* GEORGIAN CAPITAL LETTER HE */ \
+ 0x2D21, /* GEORGIAN SMALL LETTER HE */ }, \
+ { 0x10C2, /* GEORGIAN CAPITAL LETTER HIE */ \
+ 0x2D22, /* GEORGIAN SMALL LETTER HIE */ }, \
+ { 0x10C5, /* GEORGIAN CAPITAL LETTER HOE */ \
+ 0x2D25, /* GEORGIAN SMALL LETTER HOE */ }, \
+ { 0x10A8, /* GEORGIAN CAPITAL LETTER IN */ \
+ 0x2D08, /* GEORGIAN SMALL LETTER IN */ }, \
+ { 0x10BF, /* GEORGIAN CAPITAL LETTER JHAN */ \
+ 0x2D1F, /* GEORGIAN SMALL LETTER JHAN */ }, \
+ { 0x10BB, /* GEORGIAN CAPITAL LETTER JIL */ \
+ 0x2D1B, /* GEORGIAN SMALL LETTER JIL */ }, \
+ { 0x10A9, /* GEORGIAN CAPITAL LETTER KAN */ \
+ 0x2D09, /* GEORGIAN SMALL LETTER KAN */ }, \
+ { 0x10B5, /* GEORGIAN CAPITAL LETTER KHAR */ \
+ 0x2D15, /* GEORGIAN SMALL LETTER KHAR */ }, \
+ { 0x10AA, /* GEORGIAN CAPITAL LETTER LAS */ \
+ 0x2D0A, /* GEORGIAN SMALL LETTER LAS */ }, \
+ { 0x10AB, /* GEORGIAN CAPITAL LETTER MAN */ \
+ 0x2D0B, /* GEORGIAN SMALL LETTER MAN */ }, \
+ { 0x10AC, /* GEORGIAN CAPITAL LETTER NAR */ \
+ 0x2D0C, /* GEORGIAN SMALL LETTER NAR */ }, \
+ { 0x10AD, /* GEORGIAN CAPITAL LETTER ON */ \
+ 0x2D0D, /* GEORGIAN SMALL LETTER ON */ }, \
+ { 0x10AE, /* GEORGIAN CAPITAL LETTER PAR */ \
+ 0x2D0E, /* GEORGIAN SMALL LETTER PAR */ }, \
+ { 0x10B4, /* GEORGIAN CAPITAL LETTER PHAR */ \
+ 0x2D14, /* GEORGIAN SMALL LETTER PHAR */ }, \
+ { 0x10B7, /* GEORGIAN CAPITAL LETTER QAR */ \
+ 0x2D17, /* GEORGIAN SMALL LETTER QAR */ }, \
+ { 0x10B0, /* GEORGIAN CAPITAL LETTER RAE */ \
+ 0x2D10, /* GEORGIAN SMALL LETTER RAE */ }, \
+ { 0x10B1, /* GEORGIAN CAPITAL LETTER SAN */ \
+ 0x2D11, /* GEORGIAN SMALL LETTER SAN */ }, \
+ { 0x10B8, /* GEORGIAN CAPITAL LETTER SHIN */ \
+ 0x2D18, /* GEORGIAN SMALL LETTER SHIN */ }, \
+ { 0x10A7, /* GEORGIAN CAPITAL LETTER TAN */ \
+ 0x2D07, /* GEORGIAN SMALL LETTER TAN */ }, \
+ { 0x10B2, /* GEORGIAN CAPITAL LETTER TAR */ \
+ 0x2D12, /* GEORGIAN SMALL LETTER TAR */ }, \
+ { 0x10B3, /* GEORGIAN CAPITAL LETTER UN */ \
+ 0x2D13, /* GEORGIAN SMALL LETTER UN */ }, \
+ { 0x10A5, /* GEORGIAN CAPITAL LETTER VIN */ \
+ 0x2D05, /* GEORGIAN SMALL LETTER VIN */ }, \
+ { 0x10C3, /* GEORGIAN CAPITAL LETTER WE */ \
+ 0x2D23, /* GEORGIAN SMALL LETTER WE */ }, \
+ { 0x10BE, /* GEORGIAN CAPITAL LETTER XAN */ \
+ 0x2D1E, /* GEORGIAN SMALL LETTER XAN */ }, \
+ { 0x10C7, /* GEORGIAN CAPITAL LETTER YN */ \
+ 0x2D27, /* GEORGIAN SMALL LETTER YN */ }, \
+ { 0x10A6, /* GEORGIAN CAPITAL LETTER ZEN */ \
+ 0x2D06, /* GEORGIAN SMALL LETTER ZEN */ }, \
+ { 0x10AF, /* GEORGIAN CAPITAL LETTER ZHAR */ \
+ 0x2D0F, /* GEORGIAN SMALL LETTER ZHAR */ }, \
+ { 0x2C00, /* GLAGOLITIC CAPITAL LETTER AZU */ \
+ 0x2C30, /* GLAGOLITIC SMALL LETTER AZU */ }, \
+ { 0x2C28, /* GLAGOLITIC CAPITAL LETTER BIG YUS */ \
+ 0x2C58, /* GLAGOLITIC SMALL LETTER BIG YUS */ }, \
+ { 0x2C01, /* GLAGOLITIC CAPITAL LETTER BUKY */ \
+ 0x2C31, /* GLAGOLITIC SMALL LETTER BUKY */ }, \
+ { 0x2C1D, /* GLAGOLITIC CAPITAL LETTER CHRIVI */ \
+ 0x2C4D, /* GLAGOLITIC SMALL LETTER CHRIVI */ }, \
+ { 0x2C0C, /* GLAGOLITIC CAPITAL LETTER DJERVI */ \
+ 0x2C3C, /* GLAGOLITIC SMALL LETTER DJERVI */ }, \
+ { 0x2C04, /* GLAGOLITIC CAPITAL LETTER DOBRO */ \
+ 0x2C34, /* GLAGOLITIC SMALL LETTER DOBRO */ }, \
+ { 0x2C07, /* GLAGOLITIC CAPITAL LETTER DZELO */ \
+ 0x2C37, /* GLAGOLITIC SMALL LETTER DZELO */ }, \
+ { 0x2C2A, /* GLAGOLITIC CAPITAL LETTER FITA */ \
+ 0x2C5A, /* GLAGOLITIC SMALL LETTER FITA */ }, \
+ { 0x2C17, /* GLAGOLITIC CAPITAL LETTER FRITU */ \
+ 0x2C47, /* GLAGOLITIC SMALL LETTER FRITU */ }, \
+ { 0x2C03, /* GLAGOLITIC CAPITAL LETTER GLAGOLI */ \
+ 0x2C33, /* GLAGOLITIC SMALL LETTER GLAGOLI */ }, \
+ { 0x2C18, /* GLAGOLITIC CAPITAL LETTER HERU */ \
+ 0x2C48, /* GLAGOLITIC SMALL LETTER HERU */ }, \
+ { 0x2C0B, /* GLAGOLITIC CAPITAL LETTER I */ \
+ 0x2C3B, /* GLAGOLITIC SMALL LETTER I */ }, \
+ { 0x2C0A, /* GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ \
+ 0x2C3A, /* GLAGOLITIC SMALL LETTER INITIAL IZHE */ }, \
+ { 0x2C29, /* GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS */ \
+ 0x2C59, /* GLAGOLITIC SMALL LETTER IOTATED BIG YUS */ }, \
+ { 0x2C27, /* GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS */ \
+ 0x2C57, /* GLAGOLITIC SMALL LETTER IOTATED SMALL YUS */ }, \
+ { 0x2C09, /* GLAGOLITIC CAPITAL LETTER IZHE */ \
+ 0x2C39, /* GLAGOLITIC SMALL LETTER IZHE */ }, \
+ { 0x2C2B, /* GLAGOLITIC CAPITAL LETTER IZHITSA */ \
+ 0x2C5B, /* GLAGOLITIC SMALL LETTER IZHITSA */ }, \
+ { 0x2C0D, /* GLAGOLITIC CAPITAL LETTER KAKO */ \
+ 0x2C3D, /* GLAGOLITIC SMALL LETTER KAKO */ }, \
+ { 0x2C2E, /* GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE */ \
+ 0x2C5E, /* GLAGOLITIC SMALL LETTER LATINATE MYSLITE */ }, \
+ { 0x2C0E, /* GLAGOLITIC CAPITAL LETTER LJUDIJE */ \
+ 0x2C3E, /* GLAGOLITIC SMALL LETTER LJUDIJE */ }, \
+ { 0x2C0F, /* GLAGOLITIC CAPITAL LETTER MYSLITE */ \
+ 0x2C3F, /* GLAGOLITIC SMALL LETTER MYSLITE */ }, \
+ { 0x2C10, /* GLAGOLITIC CAPITAL LETTER NASHI */ \
+ 0x2C40, /* GLAGOLITIC SMALL LETTER NASHI */ }, \
+ { 0x2C11, /* GLAGOLITIC CAPITAL LETTER ONU */ \
+ 0x2C41, /* GLAGOLITIC SMALL LETTER ONU */ }, \
+ { 0x2C19, /* GLAGOLITIC CAPITAL LETTER OTU */ \
+ 0x2C49, /* GLAGOLITIC SMALL LETTER OTU */ }, \
+ { 0x2C1A, /* GLAGOLITIC CAPITAL LETTER PE */ \
+ 0x2C4A, /* GLAGOLITIC SMALL LETTER PE */ }, \
+ { 0x2C12, /* GLAGOLITIC CAPITAL LETTER POKOJI */ \
+ 0x2C42, /* GLAGOLITIC SMALL LETTER POKOJI */ }, \
+ { 0x2C13, /* GLAGOLITIC CAPITAL LETTER RITSI */ \
+ 0x2C43, /* GLAGOLITIC SMALL LETTER RITSI */ }, \
+ { 0x2C1E, /* GLAGOLITIC CAPITAL LETTER SHA */ \
+ 0x2C4E, /* GLAGOLITIC SMALL LETTER SHA */ }, \
+ { 0x2C1B, /* GLAGOLITIC CAPITAL LETTER SHTA */ \
+ 0x2C4B, /* GLAGOLITIC SMALL LETTER SHTA */ }, \
+ { 0x2C2C, /* GLAGOLITIC CAPITAL LETTER SHTAPIC */ \
+ 0x2C5C, /* GLAGOLITIC SMALL LETTER SHTAPIC */ }, \
+ { 0x2C14, /* GLAGOLITIC CAPITAL LETTER SLOVO */ \
+ 0x2C44, /* GLAGOLITIC SMALL LETTER SLOVO */ }, \
+ { 0x2C24, /* GLAGOLITIC CAPITAL LETTER SMALL YUS */ \
+ 0x2C54, /* GLAGOLITIC SMALL LETTER SMALL YUS */ }, \
+ { 0x2C25, /* GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL */ \
+ 0x2C55, /* GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL */ }, \
+ { 0x2C22, /* GLAGOLITIC CAPITAL LETTER SPIDERY HA */ \
+ 0x2C52, /* GLAGOLITIC SMALL LETTER SPIDERY HA */ }, \
+ { 0x2C2D, /* GLAGOLITIC CAPITAL LETTER TROKUTASTI A */ \
+ 0x2C5D, /* GLAGOLITIC SMALL LETTER TROKUTASTI A */ }, \
+ { 0x2C1C, /* GLAGOLITIC CAPITAL LETTER TSI */ \
+ 0x2C4C, /* GLAGOLITIC SMALL LETTER TSI */ }, \
+ { 0x2C15, /* GLAGOLITIC CAPITAL LETTER TVRIDO */ \
+ 0x2C45, /* GLAGOLITIC SMALL LETTER TVRIDO */ }, \
+ { 0x2C16, /* GLAGOLITIC CAPITAL LETTER UKU */ \
+ 0x2C46, /* GLAGOLITIC SMALL LETTER UKU */ }, \
+ { 0x2C02, /* GLAGOLITIC CAPITAL LETTER VEDE */ \
+ 0x2C32, /* GLAGOLITIC SMALL LETTER VEDE */ }, \
+ { 0x2C21, /* GLAGOLITIC CAPITAL LETTER YATI */ \
+ 0x2C51, /* GLAGOLITIC SMALL LETTER YATI */ }, \
+ { 0x2C20, /* GLAGOLITIC CAPITAL LETTER YERI */ \
+ 0x2C50, /* GLAGOLITIC SMALL LETTER YERI */ }, \
+ { 0x2C1F, /* GLAGOLITIC CAPITAL LETTER YERU */ \
+ 0x2C4F, /* GLAGOLITIC SMALL LETTER YERU */ }, \
+ { 0x2C05, /* GLAGOLITIC CAPITAL LETTER YESTU */ \
+ 0x2C35, /* GLAGOLITIC SMALL LETTER YESTU */ }, \
+ { 0x2C26, /* GLAGOLITIC CAPITAL LETTER YO */ \
+ 0x2C56, /* GLAGOLITIC SMALL LETTER YO */ }, \
+ { 0x2C23, /* GLAGOLITIC CAPITAL LETTER YU */ \
+ 0x2C53, /* GLAGOLITIC SMALL LETTER YU */ }, \
+ { 0x2C08, /* GLAGOLITIC CAPITAL LETTER ZEMLJA */ \
+ 0x2C38, /* GLAGOLITIC SMALL LETTER ZEMLJA */ }, \
+ { 0x2C06, /* GLAGOLITIC CAPITAL LETTER ZHIVETE */ \
+ 0x2C36, /* GLAGOLITIC SMALL LETTER ZHIVETE */ }, \
+ { 0x0391, /* GREEK CAPITAL LETTER ALPHA */ \
+ 0x03B1, /* GREEK SMALL LETTER ALPHA */ }, \
+ { 0x1F09, /* GREEK CAPITAL LETTER ALPHA WITH DASIA */ \
+ 0x1F01, /* GREEK SMALL LETTER ALPHA WITH DASIA */ }, \
+ { 0x1F0D, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */ \
+ 0x1F05, /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA */ }, \
+ { 0x1F0F, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */ \
+ 0x1F07, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F0B, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */ \
+ 0x1F03, /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA */ }, \
+ { 0x1FB9, /* GREEK CAPITAL LETTER ALPHA WITH MACRON */ \
+ 0x1FB1, /* GREEK SMALL LETTER ALPHA WITH MACRON */ }, \
+ { 0x1FBB, /* GREEK CAPITAL LETTER ALPHA WITH OXIA */ \
+ 0x1F71, /* GREEK SMALL LETTER ALPHA WITH OXIA */ }, \
+ { 0x1F08, /* GREEK CAPITAL LETTER ALPHA WITH PSILI */ \
+ 0x1F00, /* GREEK SMALL LETTER ALPHA WITH PSILI */ }, \
+ { 0x1F0C, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */ \
+ 0x1F04, /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA */ }, \
+ { 0x1F0E, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */ \
+ 0x1F06, /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F0A, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */ \
+ 0x1F02, /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA */ }, \
+ { 0x0386, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ \
+ 0x03AC, /* GREEK SMALL LETTER ALPHA WITH TONOS */ }, \
+ { 0x1FBA, /* GREEK CAPITAL LETTER ALPHA WITH VARIA */ \
+ 0x1F70, /* GREEK SMALL LETTER ALPHA WITH VARIA */ }, \
+ { 0x1FB8, /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */ \
+ 0x1FB0, /* GREEK SMALL LETTER ALPHA WITH VRACHY */ }, \
+ { 0x0372, /* GREEK CAPITAL LETTER ARCHAIC SAMPI */ \
+ 0x0373, /* GREEK SMALL LETTER ARCHAIC SAMPI */ }, \
+ { 0x0392, /* GREEK CAPITAL LETTER BETA */ \
+ 0x03B2, /* GREEK SMALL LETTER BETA */ }, \
+ { 0x03A7, /* GREEK CAPITAL LETTER CHI */ \
+ 0x03C7, /* GREEK SMALL LETTER CHI */ }, \
+ { 0x0394, /* GREEK CAPITAL LETTER DELTA */ \
+ 0x03B4, /* GREEK SMALL LETTER DELTA */ }, \
+ { 0x0395, /* GREEK CAPITAL LETTER EPSILON */ \
+ 0x03B5, /* GREEK SMALL LETTER EPSILON */ }, \
+ { 0x1F19, /* GREEK CAPITAL LETTER EPSILON WITH DASIA */ \
+ 0x1F11, /* GREEK SMALL LETTER EPSILON WITH DASIA */ }, \
+ { 0x1F1D, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */ \
+ 0x1F15, /* GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA */ }, \
+ { 0x1F1B, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */ \
+ 0x1F13, /* GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA */ }, \
+ { 0x1FC9, /* GREEK CAPITAL LETTER EPSILON WITH OXIA */ \
+ 0x1F73, /* GREEK SMALL LETTER EPSILON WITH OXIA */ }, \
+ { 0x1F18, /* GREEK CAPITAL LETTER EPSILON WITH PSILI */ \
+ 0x1F10, /* GREEK SMALL LETTER EPSILON WITH PSILI */ }, \
+ { 0x1F1C, /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */ \
+ 0x1F14, /* GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA */ }, \
+ { 0x1F1A, /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */ \
+ 0x1F12, /* GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA */ }, \
+ { 0x0388, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ \
+ 0x03AD, /* GREEK SMALL LETTER EPSILON WITH TONOS */ }, \
+ { 0x1FC8, /* GREEK CAPITAL LETTER EPSILON WITH VARIA */ \
+ 0x1F72, /* GREEK SMALL LETTER EPSILON WITH VARIA */ }, \
+ { 0x0397, /* GREEK CAPITAL LETTER ETA */ \
+ 0x03B7, /* GREEK SMALL LETTER ETA */ }, \
+ { 0x1F29, /* GREEK CAPITAL LETTER ETA WITH DASIA */ \
+ 0x1F21, /* GREEK SMALL LETTER ETA WITH DASIA */ }, \
+ { 0x1F2D, /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */ \
+ 0x1F25, /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA */ }, \
+ { 0x1F2F, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */ \
+ 0x1F27, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F2B, /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */ \
+ 0x1F23, /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA */ }, \
+ { 0x1FCB, /* GREEK CAPITAL LETTER ETA WITH OXIA */ \
+ 0x1F75, /* GREEK SMALL LETTER ETA WITH OXIA */ }, \
+ { 0x1F28, /* GREEK CAPITAL LETTER ETA WITH PSILI */ \
+ 0x1F20, /* GREEK SMALL LETTER ETA WITH PSILI */ }, \
+ { 0x1F2C, /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */ \
+ 0x1F24, /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA */ }, \
+ { 0x1F2E, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */ \
+ 0x1F26, /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F2A, /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */ \
+ 0x1F22, /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA */ }, \
+ { 0x0389, /* GREEK CAPITAL LETTER ETA WITH TONOS */ \
+ 0x03AE, /* GREEK SMALL LETTER ETA WITH TONOS */ }, \
+ { 0x1FCA, /* GREEK CAPITAL LETTER ETA WITH VARIA */ \
+ 0x1F74, /* GREEK SMALL LETTER ETA WITH VARIA */ }, \
+ { 0x0393, /* GREEK CAPITAL LETTER GAMMA */ \
+ 0x03B3, /* GREEK SMALL LETTER GAMMA */ }, \
+ { 0x0370, /* GREEK CAPITAL LETTER HETA */ \
+ 0x0371, /* GREEK SMALL LETTER HETA */ }, \
+ { 0x0399, /* GREEK CAPITAL LETTER IOTA */ \
+ 0x03B9, /* GREEK SMALL LETTER IOTA */ }, \
+ { 0x1F39, /* GREEK CAPITAL LETTER IOTA WITH DASIA */ \
+ 0x1F31, /* GREEK SMALL LETTER IOTA WITH DASIA */ }, \
+ { 0x1F3D, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */ \
+ 0x1F35, /* GREEK SMALL LETTER IOTA WITH DASIA AND OXIA */ }, \
+ { 0x1F3F, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */ \
+ 0x1F37, /* GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F3B, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */ \
+ 0x1F33, /* GREEK SMALL LETTER IOTA WITH DASIA AND VARIA */ }, \
+ { 0x03AA, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ \
+ 0x03CA, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA */ }, \
+ { 0x1FD9, /* GREEK CAPITAL LETTER IOTA WITH MACRON */ \
+ 0x1FD1, /* GREEK SMALL LETTER IOTA WITH MACRON */ }, \
+ { 0x1FDB, /* GREEK CAPITAL LETTER IOTA WITH OXIA */ \
+ 0x1F77, /* GREEK SMALL LETTER IOTA WITH OXIA */ }, \
+ { 0x1F38, /* GREEK CAPITAL LETTER IOTA WITH PSILI */ \
+ 0x1F30, /* GREEK SMALL LETTER IOTA WITH PSILI */ }, \
+ { 0x1F3C, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */ \
+ 0x1F34, /* GREEK SMALL LETTER IOTA WITH PSILI AND OXIA */ }, \
+ { 0x1F3E, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */ \
+ 0x1F36, /* GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F3A, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */ \
+ 0x1F32, /* GREEK SMALL LETTER IOTA WITH PSILI AND VARIA */ }, \
+ { 0x038A, /* GREEK CAPITAL LETTER IOTA WITH TONOS */ \
+ 0x03AF, /* GREEK SMALL LETTER IOTA WITH TONOS */ }, \
+ { 0x1FDA, /* GREEK CAPITAL LETTER IOTA WITH VARIA */ \
+ 0x1F76, /* GREEK SMALL LETTER IOTA WITH VARIA */ }, \
+ { 0x1FD8, /* GREEK CAPITAL LETTER IOTA WITH VRACHY */ \
+ 0x1FD0, /* GREEK SMALL LETTER IOTA WITH VRACHY */ }, \
+ { 0x039A, /* GREEK CAPITAL LETTER KAPPA */ \
+ 0x03BA, /* GREEK SMALL LETTER KAPPA */ }, \
+ { 0x039B, /* GREEK CAPITAL LETTER LAMDA */ \
+ 0x03BB, /* GREEK SMALL LETTER LAMDA */ }, \
+ { 0x039C, /* GREEK CAPITAL LETTER MU */ \
+ 0x03BC, /* GREEK SMALL LETTER MU */ }, \
+ { 0x039D, /* GREEK CAPITAL LETTER NU */ \
+ 0x03BD, /* GREEK SMALL LETTER NU */ }, \
+ { 0x03A9, /* GREEK CAPITAL LETTER OMEGA */ \
+ 0x03C9, /* GREEK SMALL LETTER OMEGA */ }, \
+ { 0x1F69, /* GREEK CAPITAL LETTER OMEGA WITH DASIA */ \
+ 0x1F61, /* GREEK SMALL LETTER OMEGA WITH DASIA */ }, \
+ { 0x1F6D, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */ \
+ 0x1F65, /* GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA */ }, \
+ { 0x1F6F, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */ \
+ 0x1F67, /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F6B, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */ \
+ 0x1F63, /* GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA */ }, \
+ { 0x1FFB, /* GREEK CAPITAL LETTER OMEGA WITH OXIA */ \
+ 0x1F7D, /* GREEK SMALL LETTER OMEGA WITH OXIA */ }, \
+ { 0x1F68, /* GREEK CAPITAL LETTER OMEGA WITH PSILI */ \
+ 0x1F60, /* GREEK SMALL LETTER OMEGA WITH PSILI */ }, \
+ { 0x1F6C, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */ \
+ 0x1F64, /* GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA */ }, \
+ { 0x1F6E, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */ \
+ 0x1F66, /* GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F6A, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */ \
+ 0x1F62, /* GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA */ }, \
+ { 0x038F, /* GREEK CAPITAL LETTER OMEGA WITH TONOS */ \
+ 0x03CE, /* GREEK SMALL LETTER OMEGA WITH TONOS */ }, \
+ { 0x1FFA, /* GREEK CAPITAL LETTER OMEGA WITH VARIA */ \
+ 0x1F7C, /* GREEK SMALL LETTER OMEGA WITH VARIA */ }, \
+ { 0x039F, /* GREEK CAPITAL LETTER OMICRON */ \
+ 0x03BF, /* GREEK SMALL LETTER OMICRON */ }, \
+ { 0x1F49, /* GREEK CAPITAL LETTER OMICRON WITH DASIA */ \
+ 0x1F41, /* GREEK SMALL LETTER OMICRON WITH DASIA */ }, \
+ { 0x1F4D, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */ \
+ 0x1F45, /* GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA */ }, \
+ { 0x1F4B, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */ \
+ 0x1F43, /* GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA */ }, \
+ { 0x1FF9, /* GREEK CAPITAL LETTER OMICRON WITH OXIA */ \
+ 0x1F79, /* GREEK SMALL LETTER OMICRON WITH OXIA */ }, \
+ { 0x1F48, /* GREEK CAPITAL LETTER OMICRON WITH PSILI */ \
+ 0x1F40, /* GREEK SMALL LETTER OMICRON WITH PSILI */ }, \
+ { 0x1F4C, /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */ \
+ 0x1F44, /* GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA */ }, \
+ { 0x1F4A, /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */ \
+ 0x1F42, /* GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA */ }, \
+ { 0x038C, /* GREEK CAPITAL LETTER OMICRON WITH TONOS */ \
+ 0x03CC, /* GREEK SMALL LETTER OMICRON WITH TONOS */ }, \
+ { 0x1FF8, /* GREEK CAPITAL LETTER OMICRON WITH VARIA */ \
+ 0x1F78, /* GREEK SMALL LETTER OMICRON WITH VARIA */ }, \
+ { 0x0376, /* GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA */ \
+ 0x0377, /* GREEK SMALL LETTER PAMPHYLIAN DIGAMMA */ }, \
+ { 0x03A6, /* GREEK CAPITAL LETTER PHI */ \
+ 0x03C6, /* GREEK SMALL LETTER PHI */ }, \
+ { 0x03A0, /* GREEK CAPITAL LETTER PI */ \
+ 0x03C0, /* GREEK SMALL LETTER PI */ }, \
+ { 0x03A8, /* GREEK CAPITAL LETTER PSI */ \
+ 0x03C8, /* GREEK SMALL LETTER PSI */ }, \
+ { 0x03A1, /* GREEK CAPITAL LETTER RHO */ \
+ 0x03C1, /* GREEK SMALL LETTER RHO */ }, \
+ { 0x1FEC, /* GREEK CAPITAL LETTER RHO WITH DASIA */ \
+ 0x1FE5, /* GREEK SMALL LETTER RHO WITH DASIA */ }, \
+ { 0x03FA, /* GREEK CAPITAL LETTER SAN */ \
+ 0x03FB, /* GREEK SMALL LETTER SAN */ }, \
+ { 0x03F7, /* GREEK CAPITAL LETTER SHO */ \
+ 0x03F8, /* GREEK SMALL LETTER SHO */ }, \
+ { 0x03A3, /* GREEK CAPITAL LETTER SIGMA */ \
+ 0x03C3, /* GREEK SMALL LETTER SIGMA */ }, \
+ { 0x03A4, /* GREEK CAPITAL LETTER TAU */ \
+ 0x03C4, /* GREEK SMALL LETTER TAU */ }, \
+ { 0x0398, /* GREEK CAPITAL LETTER THETA */ \
+ 0x03B8, /* GREEK SMALL LETTER THETA */ }, \
+ { 0x03A5, /* GREEK CAPITAL LETTER UPSILON */ \
+ 0x03C5, /* GREEK SMALL LETTER UPSILON */ }, \
+ { 0x1F59, /* GREEK CAPITAL LETTER UPSILON WITH DASIA */ \
+ 0x1F51, /* GREEK SMALL LETTER UPSILON WITH DASIA */ }, \
+ { 0x1F5D, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */ \
+ 0x1F55, /* GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA */ }, \
+ { 0x1F5F, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */ \
+ 0x1F57, /* GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F5B, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */ \
+ 0x1F53, /* GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA */ }, \
+ { 0x03AB, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ \
+ 0x03CB, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ }, \
+ { 0x1FE9, /* GREEK CAPITAL LETTER UPSILON WITH MACRON */ \
+ 0x1FE1, /* GREEK SMALL LETTER UPSILON WITH MACRON */ }, \
+ { 0x1FEB, /* GREEK CAPITAL LETTER UPSILON WITH OXIA */ \
+ 0x1F7B, /* GREEK SMALL LETTER UPSILON WITH OXIA */ }, \
+ { 0x038E, /* GREEK CAPITAL LETTER UPSILON WITH TONOS */ \
+ 0x03CD, /* GREEK SMALL LETTER UPSILON WITH TONOS */ }, \
+ { 0x1FEA, /* GREEK CAPITAL LETTER UPSILON WITH VARIA */ \
+ 0x1F7A, /* GREEK SMALL LETTER UPSILON WITH VARIA */ }, \
+ { 0x1FE8, /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */ \
+ 0x1FE0, /* GREEK SMALL LETTER UPSILON WITH VRACHY */ }, \
+ { 0x039E, /* GREEK CAPITAL LETTER XI */ \
+ 0x03BE, /* GREEK SMALL LETTER XI */ }, \
+ { 0x0396, /* GREEK CAPITAL LETTER ZETA */ \
+ 0x03B6, /* GREEK SMALL LETTER ZETA */ }, \
+ { 0x0041, /* LATIN CAPITAL LETTER A */ \
+ 0x0061, /* LATIN SMALL LETTER A */ }, \
+ { 0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ \
+ 0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ }, \
+ { 0x0102, /* LATIN CAPITAL LETTER A WITH BREVE */ \
+ 0x0103, /* LATIN SMALL LETTER A WITH BREVE */ }, \
+ { 0x1EAE, /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */ \
+ 0x1EAF, /* LATIN SMALL LETTER A WITH BREVE AND ACUTE */ }, \
+ { 0x1EB6, /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */ \
+ 0x1EB7, /* LATIN SMALL LETTER A WITH BREVE AND DOT BELOW */ }, \
+ { 0x1EB0, /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */ \
+ 0x1EB1, /* LATIN SMALL LETTER A WITH BREVE AND GRAVE */ }, \
+ { 0x1EB2, /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */ \
+ 0x1EB3, /* LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE */ }, \
+ { 0x1EB4, /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */ \
+ 0x1EB5, /* LATIN SMALL LETTER A WITH BREVE AND TILDE */ }, \
+ { 0x01CD, /* LATIN CAPITAL LETTER A WITH CARON */ \
+ 0x01CE, /* LATIN SMALL LETTER A WITH CARON */ }, \
+ { 0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ \
+ 0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ }, \
+ { 0x1EA4, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */ \
+ 0x1EA5, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE */ }, \
+ { 0x1EAC, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ \
+ 0x1EAD, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ }, \
+ { 0x1EA6, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */ \
+ 0x1EA7, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE */ }, \
+ { 0x1EA8, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ \
+ 0x1EA9, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ }, \
+ { 0x1EAA, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */ \
+ 0x1EAB, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE */ }, \
+ { 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x01DE, /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */ \
+ 0x01DF, /* LATIN SMALL LETTER A WITH DIAERESIS AND MACRON */ }, \
+ { 0x0226, /* LATIN CAPITAL LETTER A WITH DOT ABOVE */ \
+ 0x0227, /* LATIN SMALL LETTER A WITH DOT ABOVE */ }, \
+ { 0x01E0, /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */ \
+ 0x01E1, /* LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON */ }, \
+ { 0x1EA0, /* LATIN CAPITAL LETTER A WITH DOT BELOW */ \
+ 0x1EA1, /* LATIN SMALL LETTER A WITH DOT BELOW */ }, \
+ { 0x0200, /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */ \
+ 0x0201, /* LATIN SMALL LETTER A WITH DOUBLE GRAVE */ }, \
+ { 0x00C0, /* LATIN CAPITAL LETTER A WITH GRAVE */ \
+ 0x00E0, /* LATIN SMALL LETTER A WITH GRAVE */ }, \
+ { 0x1EA2, /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */ \
+ 0x1EA3, /* LATIN SMALL LETTER A WITH HOOK ABOVE */ }, \
+ { 0x0202, /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */ \
+ 0x0203, /* LATIN SMALL LETTER A WITH INVERTED BREVE */ }, \
+ { 0x0100, /* LATIN CAPITAL LETTER A WITH MACRON */ \
+ 0x0101, /* LATIN SMALL LETTER A WITH MACRON */ }, \
+ { 0x0104, /* LATIN CAPITAL LETTER A WITH OGONEK */ \
+ 0x0105, /* LATIN SMALL LETTER A WITH OGONEK */ }, \
+ { 0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ \
+ 0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ }, \
+ { 0x01FA, /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */ \
+ 0x01FB, /* LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE */ }, \
+ { 0x1E00, /* LATIN CAPITAL LETTER A WITH RING BELOW */ \
+ 0x1E01, /* LATIN SMALL LETTER A WITH RING BELOW */ }, \
+ { 0x023A, /* LATIN CAPITAL LETTER A WITH STROKE */ \
+ 0x2C65, /* LATIN SMALL LETTER A WITH STROKE */ }, \
+ { 0x00C3, /* LATIN CAPITAL LETTER A WITH TILDE */ \
+ 0x00E3, /* LATIN SMALL LETTER A WITH TILDE */ }, \
+ { 0xA732, /* LATIN CAPITAL LETTER AA */ \
+ 0xA733, /* LATIN SMALL LETTER AA */ }, \
+ { 0x00C6, /* LATIN CAPITAL LETTER AE */ \
+ 0x00E6, /* LATIN SMALL LETTER AE */ }, \
+ { 0x01FC, /* LATIN CAPITAL LETTER AE WITH ACUTE */ \
+ 0x01FD, /* LATIN SMALL LETTER AE WITH ACUTE */ }, \
+ { 0x01E2, /* LATIN CAPITAL LETTER AE WITH MACRON */ \
+ 0x01E3, /* LATIN SMALL LETTER AE WITH MACRON */ }, \
+ { 0x2C6D, /* LATIN CAPITAL LETTER ALPHA */ \
+ 0x0251, /* LATIN SMALL LETTER ALPHA */ }, \
+ { 0xA734, /* LATIN CAPITAL LETTER AO */ \
+ 0xA735, /* LATIN SMALL LETTER AO */ }, \
+ { 0xA736, /* LATIN CAPITAL LETTER AU */ \
+ 0xA737, /* LATIN SMALL LETTER AU */ }, \
+ { 0xA738, /* LATIN CAPITAL LETTER AV */ \
+ 0xA739, /* LATIN SMALL LETTER AV */ }, \
+ { 0xA73A, /* LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR */ \
+ 0xA73B, /* LATIN SMALL LETTER AV WITH HORIZONTAL BAR */ }, \
+ { 0xA73C, /* LATIN CAPITAL LETTER AY */ \
+ 0xA73D, /* LATIN SMALL LETTER AY */ }, \
+ { 0x0042, /* LATIN CAPITAL LETTER B */ \
+ 0x0062, /* LATIN SMALL LETTER B */ }, \
+ { 0x1E02, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ \
+ 0x1E03, /* LATIN SMALL LETTER B WITH DOT ABOVE */ }, \
+ { 0x1E04, /* LATIN CAPITAL LETTER B WITH DOT BELOW */ \
+ 0x1E05, /* LATIN SMALL LETTER B WITH DOT BELOW */ }, \
+ { 0xA796, /* LATIN CAPITAL LETTER B WITH FLOURISH */ \
+ 0xA797, /* LATIN SMALL LETTER B WITH FLOURISH */ }, \
+ { 0x0181, /* LATIN CAPITAL LETTER B WITH HOOK */ \
+ 0x0253, /* LATIN SMALL LETTER B WITH HOOK */ }, \
+ { 0x1E06, /* LATIN CAPITAL LETTER B WITH LINE BELOW */ \
+ 0x1E07, /* LATIN SMALL LETTER B WITH LINE BELOW */ }, \
+ { 0x0243, /* LATIN CAPITAL LETTER B WITH STROKE */ \
+ 0x0180, /* LATIN SMALL LETTER B WITH STROKE */ }, \
+ { 0x0182, /* LATIN CAPITAL LETTER B WITH TOPBAR */ \
+ 0x0183, /* LATIN SMALL LETTER B WITH TOPBAR */ }, \
+ { 0xA7B4, /* LATIN CAPITAL LETTER BETA */ \
+ 0xA7B5, /* LATIN SMALL LETTER BETA */ }, \
+ { 0xA746, /* LATIN CAPITAL LETTER BROKEN L */ \
+ 0xA747, /* LATIN SMALL LETTER BROKEN L */ }, \
+ { 0x0043, /* LATIN CAPITAL LETTER C */ \
+ 0x0063, /* LATIN SMALL LETTER C */ }, \
+ { 0x0106, /* LATIN CAPITAL LETTER C WITH ACUTE */ \
+ 0x0107, /* LATIN SMALL LETTER C WITH ACUTE */ }, \
+ { 0xA792, /* LATIN CAPITAL LETTER C WITH BAR */ \
+ 0xA793, /* LATIN SMALL LETTER C WITH BAR */ }, \
+ { 0x010C, /* LATIN CAPITAL LETTER C WITH CARON */ \
+ 0x010D, /* LATIN SMALL LETTER C WITH CARON */ }, \
+ { 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ \
+ 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ }, \
+ { 0x1E08, /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */ \
+ 0x1E09, /* LATIN SMALL LETTER C WITH CEDILLA AND ACUTE */ }, \
+ { 0x0108, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ \
+ 0x0109, /* LATIN SMALL LETTER C WITH CIRCUMFLEX */ }, \
+ { 0x010A, /* LATIN CAPITAL LETTER C WITH DOT ABOVE */ \
+ 0x010B, /* LATIN SMALL LETTER C WITH DOT ABOVE */ }, \
+ { 0x0187, /* LATIN CAPITAL LETTER C WITH HOOK */ \
+ 0x0188, /* LATIN SMALL LETTER C WITH HOOK */ }, \
+ { 0x023B, /* LATIN CAPITAL LETTER C WITH STROKE */ \
+ 0x023C, /* LATIN SMALL LETTER C WITH STROKE */ }, \
+ { 0xA7B3, /* LATIN CAPITAL LETTER CHI */ \
+ 0xAB53, /* LATIN SMALL LETTER CHI */ }, \
+ { 0xA76E, /* LATIN CAPITAL LETTER CON */ \
+ 0xA76F, /* LATIN SMALL LETTER CON */ }, \
+ { 0xA72C, /* LATIN CAPITAL LETTER CUATRILLO */ \
+ 0xA72D, /* LATIN SMALL LETTER CUATRILLO */ }, \
+ { 0xA72E, /* LATIN CAPITAL LETTER CUATRILLO WITH COMMA */ \
+ 0xA72F, /* LATIN SMALL LETTER CUATRILLO WITH COMMA */ }, \
+ { 0x0044, /* LATIN CAPITAL LETTER D */ \
+ 0x0064, /* LATIN SMALL LETTER D */ }, \
+ { 0x010E, /* LATIN CAPITAL LETTER D WITH CARON */ \
+ 0x010F, /* LATIN SMALL LETTER D WITH CARON */ }, \
+ { 0x1E10, /* LATIN CAPITAL LETTER D WITH CEDILLA */ \
+ 0x1E11, /* LATIN SMALL LETTER D WITH CEDILLA */ }, \
+ { 0x1E12, /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */ \
+ 0x1E13, /* LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW */ }, \
+ { 0x1E0A, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ \
+ 0x1E0B, /* LATIN SMALL LETTER D WITH DOT ABOVE */ }, \
+ { 0x1E0C, /* LATIN CAPITAL LETTER D WITH DOT BELOW */ \
+ 0x1E0D, /* LATIN SMALL LETTER D WITH DOT BELOW */ }, \
+ { 0x018A, /* LATIN CAPITAL LETTER D WITH HOOK */ \
+ 0x0257, /* LATIN SMALL LETTER D WITH HOOK */ }, \
+ { 0x1E0E, /* LATIN CAPITAL LETTER D WITH LINE BELOW */ \
+ 0x1E0F, /* LATIN SMALL LETTER D WITH LINE BELOW */ }, \
+ { 0x0110, /* LATIN CAPITAL LETTER D WITH STROKE */ \
+ 0x0111, /* LATIN SMALL LETTER D WITH STROKE */ }, \
+ { 0x018B, /* LATIN CAPITAL LETTER D WITH TOPBAR */ \
+ 0x018C, /* LATIN SMALL LETTER D WITH TOPBAR */ }, \
+ { 0x01F1, /* LATIN CAPITAL LETTER DZ */ \
+ 0x01F3, /* LATIN SMALL LETTER DZ */ }, \
+ { 0x01C4, /* LATIN CAPITAL LETTER DZ WITH CARON */ \
+ 0x01C6, /* LATIN SMALL LETTER DZ WITH CARON */ }, \
+ { 0x0045, /* LATIN CAPITAL LETTER E */ \
+ 0x0065, /* LATIN SMALL LETTER E */ }, \
+ { 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ \
+ 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ }, \
+ { 0x0114, /* LATIN CAPITAL LETTER E WITH BREVE */ \
+ 0x0115, /* LATIN SMALL LETTER E WITH BREVE */ }, \
+ { 0x011A, /* LATIN CAPITAL LETTER E WITH CARON */ \
+ 0x011B, /* LATIN SMALL LETTER E WITH CARON */ }, \
+ { 0x0228, /* LATIN CAPITAL LETTER E WITH CEDILLA */ \
+ 0x0229, /* LATIN SMALL LETTER E WITH CEDILLA */ }, \
+ { 0x1E1C, /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */ \
+ 0x1E1D, /* LATIN SMALL LETTER E WITH CEDILLA AND BREVE */ }, \
+ { 0x00CA, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ \
+ 0x00EA, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ }, \
+ { 0x1EBE, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */ \
+ 0x1EBF, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE */ }, \
+ { 0x1EC6, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ \
+ 0x1EC7, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ }, \
+ { 0x1EC0, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */ \
+ 0x1EC1, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE */ }, \
+ { 0x1EC2, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ \
+ 0x1EC3, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ }, \
+ { 0x1EC4, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */ \
+ 0x1EC5, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE */ }, \
+ { 0x1E18, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */ \
+ 0x1E19, /* LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW */ }, \
+ { 0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ \
+ 0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ }, \
+ { 0x0116, /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ \
+ 0x0117, /* LATIN SMALL LETTER E WITH DOT ABOVE */ }, \
+ { 0x1EB8, /* LATIN CAPITAL LETTER E WITH DOT BELOW */ \
+ 0x1EB9, /* LATIN SMALL LETTER E WITH DOT BELOW */ }, \
+ { 0x0204, /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */ \
+ 0x0205, /* LATIN SMALL LETTER E WITH DOUBLE GRAVE */ }, \
+ { 0x00C8, /* LATIN CAPITAL LETTER E WITH GRAVE */ \
+ 0x00E8, /* LATIN SMALL LETTER E WITH GRAVE */ }, \
+ { 0x1EBA, /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */ \
+ 0x1EBB, /* LATIN SMALL LETTER E WITH HOOK ABOVE */ }, \
+ { 0x0206, /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */ \
+ 0x0207, /* LATIN SMALL LETTER E WITH INVERTED BREVE */ }, \
+ { 0x0112, /* LATIN CAPITAL LETTER E WITH MACRON */ \
+ 0x0113, /* LATIN SMALL LETTER E WITH MACRON */ }, \
+ { 0x1E16, /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */ \
+ 0x1E17, /* LATIN SMALL LETTER E WITH MACRON AND ACUTE */ }, \
+ { 0x1E14, /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */ \
+ 0x1E15, /* LATIN SMALL LETTER E WITH MACRON AND GRAVE */ }, \
+ { 0x0118, /* LATIN CAPITAL LETTER E WITH OGONEK */ \
+ 0x0119, /* LATIN SMALL LETTER E WITH OGONEK */ }, \
+ { 0x0246, /* LATIN CAPITAL LETTER E WITH STROKE */ \
+ 0x0247, /* LATIN SMALL LETTER E WITH STROKE */ }, \
+ { 0x1EBC, /* LATIN CAPITAL LETTER E WITH TILDE */ \
+ 0x1EBD, /* LATIN SMALL LETTER E WITH TILDE */ }, \
+ { 0x1E1A, /* LATIN CAPITAL LETTER E WITH TILDE BELOW */ \
+ 0x1E1B, /* LATIN SMALL LETTER E WITH TILDE BELOW */ }, \
+ { 0xA724, /* LATIN CAPITAL LETTER EGYPTOLOGICAL AIN */ \
+ 0xA725, /* LATIN SMALL LETTER EGYPTOLOGICAL AIN */ }, \
+ { 0xA722, /* LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF */ \
+ 0xA723, /* LATIN SMALL LETTER EGYPTOLOGICAL ALEF */ }, \
+ { 0x014A, /* LATIN CAPITAL LETTER ENG */ \
+ 0x014B, /* LATIN SMALL LETTER ENG */ }, \
+ { 0x01A9, /* LATIN CAPITAL LETTER ESH */ \
+ 0x0283, /* LATIN SMALL LETTER ESH */ }, \
+ { 0xA76A, /* LATIN CAPITAL LETTER ET */ \
+ 0xA76B, /* LATIN SMALL LETTER ET */ }, \
+ { 0x00D0, /* LATIN CAPITAL LETTER ETH */ \
+ 0x00F0, /* LATIN SMALL LETTER ETH */ }, \
+ { 0x01B7, /* LATIN CAPITAL LETTER EZH */ \
+ 0x0292, /* LATIN SMALL LETTER EZH */ }, \
+ { 0x01B8, /* LATIN CAPITAL LETTER EZH REVERSED */ \
+ 0x01B9, /* LATIN SMALL LETTER EZH REVERSED */ }, \
+ { 0x01EE, /* LATIN CAPITAL LETTER EZH WITH CARON */ \
+ 0x01EF, /* LATIN SMALL LETTER EZH WITH CARON */ }, \
+ { 0x0046, /* LATIN CAPITAL LETTER F */ \
+ 0x0066, /* LATIN SMALL LETTER F */ }, \
+ { 0x1E1E, /* LATIN CAPITAL LETTER F WITH DOT ABOVE */ \
+ 0x1E1F, /* LATIN SMALL LETTER F WITH DOT ABOVE */ }, \
+ { 0x0191, /* LATIN CAPITAL LETTER F WITH HOOK */ \
+ 0x0192, /* LATIN SMALL LETTER F WITH HOOK */ }, \
+ { 0xA798, /* LATIN CAPITAL LETTER F WITH STROKE */ \
+ 0xA799, /* LATIN SMALL LETTER F WITH STROKE */ }, \
+ { 0x0047, /* LATIN CAPITAL LETTER G */ \
+ 0x0067, /* LATIN SMALL LETTER G */ }, \
+ { 0x01F4, /* LATIN CAPITAL LETTER G WITH ACUTE */ \
+ 0x01F5, /* LATIN SMALL LETTER G WITH ACUTE */ }, \
+ { 0x011E, /* LATIN CAPITAL LETTER G WITH BREVE */ \
+ 0x011F, /* LATIN SMALL LETTER G WITH BREVE */ }, \
+ { 0x01E6, /* LATIN CAPITAL LETTER G WITH CARON */ \
+ 0x01E7, /* LATIN SMALL LETTER G WITH CARON */ }, \
+ { 0x0122, /* LATIN CAPITAL LETTER G WITH CEDILLA */ \
+ 0x0123, /* LATIN SMALL LETTER G WITH CEDILLA */ }, \
+ { 0x011C, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ \
+ 0x011D, /* LATIN SMALL LETTER G WITH CIRCUMFLEX */ }, \
+ { 0x0120, /* LATIN CAPITAL LETTER G WITH DOT ABOVE */ \
+ 0x0121, /* LATIN SMALL LETTER G WITH DOT ABOVE */ }, \
+ { 0x0193, /* LATIN CAPITAL LETTER G WITH HOOK */ \
+ 0x0260, /* LATIN SMALL LETTER G WITH HOOK */ }, \
+ { 0x1E20, /* LATIN CAPITAL LETTER G WITH MACRON */ \
+ 0x1E21, /* LATIN SMALL LETTER G WITH MACRON */ }, \
+ { 0xA7A0, /* LATIN CAPITAL LETTER G WITH OBLIQUE STROKE */ \
+ 0xA7A1, /* LATIN SMALL LETTER G WITH OBLIQUE STROKE */ }, \
+ { 0x01E4, /* LATIN CAPITAL LETTER G WITH STROKE */ \
+ 0x01E5, /* LATIN SMALL LETTER G WITH STROKE */ }, \
+ { 0x0194, /* LATIN CAPITAL LETTER GAMMA */ \
+ 0x0263, /* LATIN SMALL LETTER GAMMA */ }, \
+ { 0x0241, /* LATIN CAPITAL LETTER GLOTTAL STOP */ \
+ 0x0242, /* LATIN SMALL LETTER GLOTTAL STOP */ }, \
+ { 0x0048, /* LATIN CAPITAL LETTER H */ \
+ 0x0068, /* LATIN SMALL LETTER H */ }, \
+ { 0x1E2A, /* LATIN CAPITAL LETTER H WITH BREVE BELOW */ \
+ 0x1E2B, /* LATIN SMALL LETTER H WITH BREVE BELOW */ }, \
+ { 0x021E, /* LATIN CAPITAL LETTER H WITH CARON */ \
+ 0x021F, /* LATIN SMALL LETTER H WITH CARON */ }, \
+ { 0x1E28, /* LATIN CAPITAL LETTER H WITH CEDILLA */ \
+ 0x1E29, /* LATIN SMALL LETTER H WITH CEDILLA */ }, \
+ { 0x0124, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ \
+ 0x0125, /* LATIN SMALL LETTER H WITH CIRCUMFLEX */ }, \
+ { 0x2C67, /* LATIN CAPITAL LETTER H WITH DESCENDER */ \
+ 0x2C68, /* LATIN SMALL LETTER H WITH DESCENDER */ }, \
+ { 0x1E26, /* LATIN CAPITAL LETTER H WITH DIAERESIS */ \
+ 0x1E27, /* LATIN SMALL LETTER H WITH DIAERESIS */ }, \
+ { 0x1E22, /* LATIN CAPITAL LETTER H WITH DOT ABOVE */ \
+ 0x1E23, /* LATIN SMALL LETTER H WITH DOT ABOVE */ }, \
+ { 0x1E24, /* LATIN CAPITAL LETTER H WITH DOT BELOW */ \
+ 0x1E25, /* LATIN SMALL LETTER H WITH DOT BELOW */ }, \
+ { 0xA7AA, /* LATIN CAPITAL LETTER H WITH HOOK */ \
+ 0x0266, /* LATIN SMALL LETTER H WITH HOOK */ }, \
+ { 0x0126, /* LATIN CAPITAL LETTER H WITH STROKE */ \
+ 0x0127, /* LATIN SMALL LETTER H WITH STROKE */ }, \
+ { 0x2C75, /* LATIN CAPITAL LETTER HALF H */ \
+ 0x2C76, /* LATIN SMALL LETTER HALF H */ }, \
+ { 0xA726, /* LATIN CAPITAL LETTER HENG */ \
+ 0xA727, /* LATIN SMALL LETTER HENG */ }, \
+ { 0x0049, /* LATIN CAPITAL LETTER I */ \
+ 0x0069, /* LATIN SMALL LETTER I */ }, \
+ { 0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ \
+ 0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ }, \
+ { 0x012C, /* LATIN CAPITAL LETTER I WITH BREVE */ \
+ 0x012D, /* LATIN SMALL LETTER I WITH BREVE */ }, \
+ { 0x01CF, /* LATIN CAPITAL LETTER I WITH CARON */ \
+ 0x01D0, /* LATIN SMALL LETTER I WITH CARON */ }, \
+ { 0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ \
+ 0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ }, \
+ { 0x00CF, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ \
+ 0x00EF, /* LATIN SMALL LETTER I WITH DIAERESIS */ }, \
+ { 0x1E2E, /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */ \
+ 0x1E2F, /* LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE */ }, \
+ { 0x1ECA, /* LATIN CAPITAL LETTER I WITH DOT BELOW */ \
+ 0x1ECB, /* LATIN SMALL LETTER I WITH DOT BELOW */ }, \
+ { 0x0208, /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */ \
+ 0x0209, /* LATIN SMALL LETTER I WITH DOUBLE GRAVE */ }, \
+ { 0x00CC, /* LATIN CAPITAL LETTER I WITH GRAVE */ \
+ 0x00EC, /* LATIN SMALL LETTER I WITH GRAVE */ }, \
+ { 0x1EC8, /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */ \
+ 0x1EC9, /* LATIN SMALL LETTER I WITH HOOK ABOVE */ }, \
+ { 0x020A, /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */ \
+ 0x020B, /* LATIN SMALL LETTER I WITH INVERTED BREVE */ }, \
+ { 0x012A, /* LATIN CAPITAL LETTER I WITH MACRON */ \
+ 0x012B, /* LATIN SMALL LETTER I WITH MACRON */ }, \
+ { 0x012E, /* LATIN CAPITAL LETTER I WITH OGONEK */ \
+ 0x012F, /* LATIN SMALL LETTER I WITH OGONEK */ }, \
+ { 0x0197, /* LATIN CAPITAL LETTER I WITH STROKE */ \
+ 0x0268, /* LATIN SMALL LETTER I WITH STROKE */ }, \
+ { 0x0128, /* LATIN CAPITAL LETTER I WITH TILDE */ \
+ 0x0129, /* LATIN SMALL LETTER I WITH TILDE */ }, \
+ { 0x1E2C, /* LATIN CAPITAL LETTER I WITH TILDE BELOW */ \
+ 0x1E2D, /* LATIN SMALL LETTER I WITH TILDE BELOW */ }, \
+ { 0xA779, /* LATIN CAPITAL LETTER INSULAR D */ \
+ 0xA77A, /* LATIN SMALL LETTER INSULAR D */ }, \
+ { 0xA77B, /* LATIN CAPITAL LETTER INSULAR F */ \
+ 0xA77C, /* LATIN SMALL LETTER INSULAR F */ }, \
+ { 0xA77D, /* LATIN CAPITAL LETTER INSULAR G */ \
+ 0x1D79, /* LATIN SMALL LETTER INSULAR G */ }, \
+ { 0xA782, /* LATIN CAPITAL LETTER INSULAR R */ \
+ 0xA783, /* LATIN SMALL LETTER INSULAR R */ }, \
+ { 0xA784, /* LATIN CAPITAL LETTER INSULAR S */ \
+ 0xA785, /* LATIN SMALL LETTER INSULAR S */ }, \
+ { 0xA786, /* LATIN CAPITAL LETTER INSULAR T */ \
+ 0xA787, /* LATIN SMALL LETTER INSULAR T */ }, \
+ { 0x0196, /* LATIN CAPITAL LETTER IOTA */ \
+ 0x0269, /* LATIN SMALL LETTER IOTA */ }, \
+ { 0xA76C, /* LATIN CAPITAL LETTER IS */ \
+ 0xA76D, /* LATIN SMALL LETTER IS */ }, \
+ { 0x004A, /* LATIN CAPITAL LETTER J */ \
+ 0x006A, /* LATIN SMALL LETTER J */ }, \
+ { 0x0134, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ \
+ 0x0135, /* LATIN SMALL LETTER J WITH CIRCUMFLEX */ }, \
+ { 0xA7B2, /* LATIN CAPITAL LETTER J WITH CROSSED-TAIL */ \
+ 0x029D, /* LATIN SMALL LETTER J WITH CROSSED-TAIL */ }, \
+ { 0x0248, /* LATIN CAPITAL LETTER J WITH STROKE */ \
+ 0x0249, /* LATIN SMALL LETTER J WITH STROKE */ }, \
+ { 0x004B, /* LATIN CAPITAL LETTER K */ \
+ 0x006B, /* LATIN SMALL LETTER K */ }, \
+ { 0x1E30, /* LATIN CAPITAL LETTER K WITH ACUTE */ \
+ 0x1E31, /* LATIN SMALL LETTER K WITH ACUTE */ }, \
+ { 0x01E8, /* LATIN CAPITAL LETTER K WITH CARON */ \
+ 0x01E9, /* LATIN SMALL LETTER K WITH CARON */ }, \
+ { 0x0136, /* LATIN CAPITAL LETTER K WITH CEDILLA */ \
+ 0x0137, /* LATIN SMALL LETTER K WITH CEDILLA */ }, \
+ { 0x2C69, /* LATIN CAPITAL LETTER K WITH DESCENDER */ \
+ 0x2C6A, /* LATIN SMALL LETTER K WITH DESCENDER */ }, \
+ { 0xA742, /* LATIN CAPITAL LETTER K WITH DIAGONAL STROKE */ \
+ 0xA743, /* LATIN SMALL LETTER K WITH DIAGONAL STROKE */ }, \
+ { 0x1E32, /* LATIN CAPITAL LETTER K WITH DOT BELOW */ \
+ 0x1E33, /* LATIN SMALL LETTER K WITH DOT BELOW */ }, \
+ { 0x0198, /* LATIN CAPITAL LETTER K WITH HOOK */ \
+ 0x0199, /* LATIN SMALL LETTER K WITH HOOK */ }, \
+ { 0x1E34, /* LATIN CAPITAL LETTER K WITH LINE BELOW */ \
+ 0x1E35, /* LATIN SMALL LETTER K WITH LINE BELOW */ }, \
+ { 0xA7A2, /* LATIN CAPITAL LETTER K WITH OBLIQUE STROKE */ \
+ 0xA7A3, /* LATIN SMALL LETTER K WITH OBLIQUE STROKE */ }, \
+ { 0xA740, /* LATIN CAPITAL LETTER K WITH STROKE */ \
+ 0xA741, /* LATIN SMALL LETTER K WITH STROKE */ }, \
+ { 0xA744, /* LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE */ \
+ 0xA745, /* LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE */ }, \
+ { 0x004C, /* LATIN CAPITAL LETTER L */ \
+ 0x006C, /* LATIN SMALL LETTER L */ }, \
+ { 0x0139, /* LATIN CAPITAL LETTER L WITH ACUTE */ \
+ 0x013A, /* LATIN SMALL LETTER L WITH ACUTE */ }, \
+ { 0x023D, /* LATIN CAPITAL LETTER L WITH BAR */ \
+ 0x019A, /* LATIN SMALL LETTER L WITH BAR */ }, \
+ { 0xA7AD, /* LATIN CAPITAL LETTER L WITH BELT */ \
+ 0x026C, /* LATIN SMALL LETTER L WITH BELT */ }, \
+ { 0x013D, /* LATIN CAPITAL LETTER L WITH CARON */ \
+ 0x013E, /* LATIN SMALL LETTER L WITH CARON */ }, \
+ { 0x013B, /* LATIN CAPITAL LETTER L WITH CEDILLA */ \
+ 0x013C, /* LATIN SMALL LETTER L WITH CEDILLA */ }, \
+ { 0x1E3C, /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */ \
+ 0x1E3D, /* LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW */ }, \
+ { 0x1E36, /* LATIN CAPITAL LETTER L WITH DOT BELOW */ \
+ 0x1E37, /* LATIN SMALL LETTER L WITH DOT BELOW */ }, \
+ { 0x1E38, /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */ \
+ 0x1E39, /* LATIN SMALL LETTER L WITH DOT BELOW AND MACRON */ }, \
+ { 0x2C60, /* LATIN CAPITAL LETTER L WITH DOUBLE BAR */ \
+ 0x2C61, /* LATIN SMALL LETTER L WITH DOUBLE BAR */ }, \
+ { 0xA748, /* LATIN CAPITAL LETTER L WITH HIGH STROKE */ \
+ 0xA749, /* LATIN SMALL LETTER L WITH HIGH STROKE */ }, \
+ { 0x1E3A, /* LATIN CAPITAL LETTER L WITH LINE BELOW */ \
+ 0x1E3B, /* LATIN SMALL LETTER L WITH LINE BELOW */ }, \
+ { 0x013F, /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */ \
+ 0x0140, /* LATIN SMALL LETTER L WITH MIDDLE DOT */ }, \
+ { 0x2C62, /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE */ \
+ 0x026B, /* LATIN SMALL LETTER L WITH MIDDLE TILDE */ }, \
+ { 0x0141, /* LATIN CAPITAL LETTER L WITH STROKE */ \
+ 0x0142, /* LATIN SMALL LETTER L WITH STROKE */ }, \
+ { 0x01C7, /* LATIN CAPITAL LETTER LJ */ \
+ 0x01C9, /* LATIN SMALL LETTER LJ */ }, \
+ { 0x004D, /* LATIN CAPITAL LETTER M */ \
+ 0x006D, /* LATIN SMALL LETTER M */ }, \
+ { 0x1E3E, /* LATIN CAPITAL LETTER M WITH ACUTE */ \
+ 0x1E3F, /* LATIN SMALL LETTER M WITH ACUTE */ }, \
+ { 0x1E40, /* LATIN CAPITAL LETTER M WITH DOT ABOVE */ \
+ 0x1E41, /* LATIN SMALL LETTER M WITH DOT ABOVE */ }, \
+ { 0x1E42, /* LATIN CAPITAL LETTER M WITH DOT BELOW */ \
+ 0x1E43, /* LATIN SMALL LETTER M WITH DOT BELOW */ }, \
+ { 0x2C6E, /* LATIN CAPITAL LETTER M WITH HOOK */ \
+ 0x0271, /* LATIN SMALL LETTER M WITH HOOK */ }, \
+ { 0x1EFA, /* LATIN CAPITAL LETTER MIDDLE-WELSH LL */ \
+ 0x1EFB, /* LATIN SMALL LETTER MIDDLE-WELSH LL */ }, \
+ { 0x1EFC, /* LATIN CAPITAL LETTER MIDDLE-WELSH V */ \
+ 0x1EFD, /* LATIN SMALL LETTER MIDDLE-WELSH V */ }, \
+ { 0x004E, /* LATIN CAPITAL LETTER N */ \
+ 0x006E, /* LATIN SMALL LETTER N */ }, \
+ { 0x0143, /* LATIN CAPITAL LETTER N WITH ACUTE */ \
+ 0x0144, /* LATIN SMALL LETTER N WITH ACUTE */ }, \
+ { 0x0147, /* LATIN CAPITAL LETTER N WITH CARON */ \
+ 0x0148, /* LATIN SMALL LETTER N WITH CARON */ }, \
+ { 0x0145, /* LATIN CAPITAL LETTER N WITH CEDILLA */ \
+ 0x0146, /* LATIN SMALL LETTER N WITH CEDILLA */ }, \
+ { 0x1E4A, /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */ \
+ 0x1E4B, /* LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW */ }, \
+ { 0xA790, /* LATIN CAPITAL LETTER N WITH DESCENDER */ \
+ 0xA791, /* LATIN SMALL LETTER N WITH DESCENDER */ }, \
+ { 0x1E44, /* LATIN CAPITAL LETTER N WITH DOT ABOVE */ \
+ 0x1E45, /* LATIN SMALL LETTER N WITH DOT ABOVE */ }, \
+ { 0x1E46, /* LATIN CAPITAL LETTER N WITH DOT BELOW */ \
+ 0x1E47, /* LATIN SMALL LETTER N WITH DOT BELOW */ }, \
+ { 0x01F8, /* LATIN CAPITAL LETTER N WITH GRAVE */ \
+ 0x01F9, /* LATIN SMALL LETTER N WITH GRAVE */ }, \
+ { 0x019D, /* LATIN CAPITAL LETTER N WITH LEFT HOOK */ \
+ 0x0272, /* LATIN SMALL LETTER N WITH LEFT HOOK */ }, \
+ { 0x1E48, /* LATIN CAPITAL LETTER N WITH LINE BELOW */ \
+ 0x1E49, /* LATIN SMALL LETTER N WITH LINE BELOW */ }, \
+ { 0x0220, /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG */ \
+ 0x019E, /* LATIN SMALL LETTER N WITH LONG RIGHT LEG */ }, \
+ { 0xA7A4, /* LATIN CAPITAL LETTER N WITH OBLIQUE STROKE */ \
+ 0xA7A5, /* LATIN SMALL LETTER N WITH OBLIQUE STROKE */ }, \
+ { 0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ \
+ 0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ }, \
+ { 0x01CA, /* LATIN CAPITAL LETTER NJ */ \
+ 0x01CC, /* LATIN SMALL LETTER NJ */ }, \
+ { 0x004F, /* LATIN CAPITAL LETTER O */ \
+ 0x006F, /* LATIN SMALL LETTER O */ }, \
+ { 0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ \
+ 0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ }, \
+ { 0x014E, /* LATIN CAPITAL LETTER O WITH BREVE */ \
+ 0x014F, /* LATIN SMALL LETTER O WITH BREVE */ }, \
+ { 0x01D1, /* LATIN CAPITAL LETTER O WITH CARON */ \
+ 0x01D2, /* LATIN SMALL LETTER O WITH CARON */ }, \
+ { 0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ \
+ 0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ }, \
+ { 0x1ED0, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */ \
+ 0x1ED1, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE */ }, \
+ { 0x1ED8, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ \
+ 0x1ED9, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ }, \
+ { 0x1ED2, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */ \
+ 0x1ED3, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE */ }, \
+ { 0x1ED4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ \
+ 0x1ED5, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ }, \
+ { 0x1ED6, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */ \
+ 0x1ED7, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE */ }, \
+ { 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x022A, /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON */ \
+ 0x022B, /* LATIN SMALL LETTER O WITH DIAERESIS AND MACRON */ }, \
+ { 0x022E, /* LATIN CAPITAL LETTER O WITH DOT ABOVE */ \
+ 0x022F, /* LATIN SMALL LETTER O WITH DOT ABOVE */ }, \
+ { 0x0230, /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON */ \
+ 0x0231, /* LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON */ }, \
+ { 0x1ECC, /* LATIN CAPITAL LETTER O WITH DOT BELOW */ \
+ 0x1ECD, /* LATIN SMALL LETTER O WITH DOT BELOW */ }, \
+ { 0x0150, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ \
+ 0x0151, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */ }, \
+ { 0x020C, /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */ \
+ 0x020D, /* LATIN SMALL LETTER O WITH DOUBLE GRAVE */ }, \
+ { 0x00D2, /* LATIN CAPITAL LETTER O WITH GRAVE */ \
+ 0x00F2, /* LATIN SMALL LETTER O WITH GRAVE */ }, \
+ { 0x1ECE, /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */ \
+ 0x1ECF, /* LATIN SMALL LETTER O WITH HOOK ABOVE */ }, \
+ { 0x01A0, /* LATIN CAPITAL LETTER O WITH HORN */ \
+ 0x01A1, /* LATIN SMALL LETTER O WITH HORN */ }, \
+ { 0x1EDA, /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */ \
+ 0x1EDB, /* LATIN SMALL LETTER O WITH HORN AND ACUTE */ }, \
+ { 0x1EE2, /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */ \
+ 0x1EE3, /* LATIN SMALL LETTER O WITH HORN AND DOT BELOW */ }, \
+ { 0x1EDC, /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */ \
+ 0x1EDD, /* LATIN SMALL LETTER O WITH HORN AND GRAVE */ }, \
+ { 0x1EDE, /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */ \
+ 0x1EDF, /* LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE */ }, \
+ { 0x1EE0, /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */ \
+ 0x1EE1, /* LATIN SMALL LETTER O WITH HORN AND TILDE */ }, \
+ { 0x020E, /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */ \
+ 0x020F, /* LATIN SMALL LETTER O WITH INVERTED BREVE */ }, \
+ { 0xA74A, /* LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY */ \
+ 0xA74B, /* LATIN SMALL LETTER O WITH LONG STROKE OVERLAY */ }, \
+ { 0xA74C, /* LATIN CAPITAL LETTER O WITH LOOP */ \
+ 0xA74D, /* LATIN SMALL LETTER O WITH LOOP */ }, \
+ { 0x014C, /* LATIN CAPITAL LETTER O WITH MACRON */ \
+ 0x014D, /* LATIN SMALL LETTER O WITH MACRON */ }, \
+ { 0x1E52, /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */ \
+ 0x1E53, /* LATIN SMALL LETTER O WITH MACRON AND ACUTE */ }, \
+ { 0x1E50, /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */ \
+ 0x1E51, /* LATIN SMALL LETTER O WITH MACRON AND GRAVE */ }, \
+ { 0x01EA, /* LATIN CAPITAL LETTER O WITH OGONEK */ \
+ 0x01EB, /* LATIN SMALL LETTER O WITH OGONEK */ }, \
+ { 0x01EC, /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */ \
+ 0x01ED, /* LATIN SMALL LETTER O WITH OGONEK AND MACRON */ }, \
+ { 0x00D8, /* LATIN CAPITAL LETTER O WITH STROKE */ \
+ 0x00F8, /* LATIN SMALL LETTER O WITH STROKE */ }, \
+ { 0x01FE, /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */ \
+ 0x01FF, /* LATIN SMALL LETTER O WITH STROKE AND ACUTE */ }, \
+ { 0x00D5, /* LATIN CAPITAL LETTER O WITH TILDE */ \
+ 0x00F5, /* LATIN SMALL LETTER O WITH TILDE */ }, \
+ { 0x1E4C, /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */ \
+ 0x1E4D, /* LATIN SMALL LETTER O WITH TILDE AND ACUTE */ }, \
+ { 0x1E4E, /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */ \
+ 0x1E4F, /* LATIN SMALL LETTER O WITH TILDE AND DIAERESIS */ }, \
+ { 0x022C, /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON */ \
+ 0x022D, /* LATIN SMALL LETTER O WITH TILDE AND MACRON */ }, \
+ { 0x01A2, /* LATIN CAPITAL LETTER OI */ \
+ 0x01A3, /* LATIN SMALL LETTER OI */ }, \
+ { 0xA7B6, /* LATIN CAPITAL LETTER OMEGA */ \
+ 0xA7B7, /* LATIN SMALL LETTER OMEGA */ }, \
+ { 0xA74E, /* LATIN CAPITAL LETTER OO */ \
+ 0xA74F, /* LATIN SMALL LETTER OO */ }, \
+ { 0x0190, /* LATIN CAPITAL LETTER OPEN E */ \
+ 0x025B, /* LATIN SMALL LETTER OPEN E */ }, \
+ { 0x0186, /* LATIN CAPITAL LETTER OPEN O */ \
+ 0x0254, /* LATIN SMALL LETTER OPEN O */ }, \
+ { 0x0222, /* LATIN CAPITAL LETTER OU */ \
+ 0x0223, /* LATIN SMALL LETTER OU */ }, \
+ { 0x0050, /* LATIN CAPITAL LETTER P */ \
+ 0x0070, /* LATIN SMALL LETTER P */ }, \
+ { 0x1E54, /* LATIN CAPITAL LETTER P WITH ACUTE */ \
+ 0x1E55, /* LATIN SMALL LETTER P WITH ACUTE */ }, \
+ { 0x1E56, /* LATIN CAPITAL LETTER P WITH DOT ABOVE */ \
+ 0x1E57, /* LATIN SMALL LETTER P WITH DOT ABOVE */ }, \
+ { 0xA752, /* LATIN CAPITAL LETTER P WITH FLOURISH */ \
+ 0xA753, /* LATIN SMALL LETTER P WITH FLOURISH */ }, \
+ { 0x01A4, /* LATIN CAPITAL LETTER P WITH HOOK */ \
+ 0x01A5, /* LATIN SMALL LETTER P WITH HOOK */ }, \
+ { 0xA754, /* LATIN CAPITAL LETTER P WITH SQUIRREL TAIL */ \
+ 0xA755, /* LATIN SMALL LETTER P WITH SQUIRREL TAIL */ }, \
+ { 0x2C63, /* LATIN CAPITAL LETTER P WITH STROKE */ \
+ 0x1D7D, /* LATIN SMALL LETTER P WITH STROKE */ }, \
+ { 0xA750, /* LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER */ \
+ 0xA751, /* LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER */ }, \
+ { 0x0051, /* LATIN CAPITAL LETTER Q */ \
+ 0x0071, /* LATIN SMALL LETTER Q */ }, \
+ { 0xA758, /* LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE */ \
+ 0xA759, /* LATIN SMALL LETTER Q WITH DIAGONAL STROKE */ }, \
+ { 0xA756, /* LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER */ \
+ 0xA757, /* LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER */ }, \
+ { 0x0052, /* LATIN CAPITAL LETTER R */ \
+ 0x0072, /* LATIN SMALL LETTER R */ }, \
+ { 0xA75A, /* LATIN CAPITAL LETTER R ROTUNDA */ \
+ 0xA75B, /* LATIN SMALL LETTER R ROTUNDA */ }, \
+ { 0x0154, /* LATIN CAPITAL LETTER R WITH ACUTE */ \
+ 0x0155, /* LATIN SMALL LETTER R WITH ACUTE */ }, \
+ { 0x0158, /* LATIN CAPITAL LETTER R WITH CARON */ \
+ 0x0159, /* LATIN SMALL LETTER R WITH CARON */ }, \
+ { 0x0156, /* LATIN CAPITAL LETTER R WITH CEDILLA */ \
+ 0x0157, /* LATIN SMALL LETTER R WITH CEDILLA */ }, \
+ { 0x1E58, /* LATIN CAPITAL LETTER R WITH DOT ABOVE */ \
+ 0x1E59, /* LATIN SMALL LETTER R WITH DOT ABOVE */ }, \
+ { 0x1E5A, /* LATIN CAPITAL LETTER R WITH DOT BELOW */ \
+ 0x1E5B, /* LATIN SMALL LETTER R WITH DOT BELOW */ }, \
+ { 0x1E5C, /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */ \
+ 0x1E5D, /* LATIN SMALL LETTER R WITH DOT BELOW AND MACRON */ }, \
+ { 0x0210, /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */ \
+ 0x0211, /* LATIN SMALL LETTER R WITH DOUBLE GRAVE */ }, \
+ { 0x0212, /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */ \
+ 0x0213, /* LATIN SMALL LETTER R WITH INVERTED BREVE */ }, \
+ { 0x1E5E, /* LATIN CAPITAL LETTER R WITH LINE BELOW */ \
+ 0x1E5F, /* LATIN SMALL LETTER R WITH LINE BELOW */ }, \
+ { 0xA7A6, /* LATIN CAPITAL LETTER R WITH OBLIQUE STROKE */ \
+ 0xA7A7, /* LATIN SMALL LETTER R WITH OBLIQUE STROKE */ }, \
+ { 0x024C, /* LATIN CAPITAL LETTER R WITH STROKE */ \
+ 0x024D, /* LATIN SMALL LETTER R WITH STROKE */ }, \
+ { 0x2C64, /* LATIN CAPITAL LETTER R WITH TAIL */ \
+ 0x027D, /* LATIN SMALL LETTER R WITH TAIL */ }, \
+ { 0xA73E, /* LATIN CAPITAL LETTER REVERSED C WITH DOT */ \
+ 0xA73F, /* LATIN SMALL LETTER REVERSED C WITH DOT */ }, \
+ { 0x018E, /* LATIN CAPITAL LETTER REVERSED E */ \
+ 0x0258, /* LATIN SMALL LETTER REVERSED E */ }, \
+ { 0xA7AB, /* LATIN CAPITAL LETTER REVERSED OPEN E */ \
+ 0x025C, /* LATIN SMALL LETTER REVERSED OPEN E */ }, \
+ { 0xA75C, /* LATIN CAPITAL LETTER RUM ROTUNDA */ \
+ 0xA75D, /* LATIN SMALL LETTER RUM ROTUNDA */ }, \
+ { 0x0053, /* LATIN CAPITAL LETTER S */ \
+ 0x0073, /* LATIN SMALL LETTER S */ }, \
+ { 0x015A, /* LATIN CAPITAL LETTER S WITH ACUTE */ \
+ 0x015B, /* LATIN SMALL LETTER S WITH ACUTE */ }, \
+ { 0x1E64, /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */ \
+ 0x1E65, /* LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE */ }, \
+ { 0x0160, /* LATIN CAPITAL LETTER S WITH CARON */ \
+ 0x0161, /* LATIN SMALL LETTER S WITH CARON */ }, \
+ { 0x1E66, /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */ \
+ 0x1E67, /* LATIN SMALL LETTER S WITH CARON AND DOT ABOVE */ }, \
+ { 0x015E, /* LATIN CAPITAL LETTER S WITH CEDILLA */ \
+ 0x015F, /* LATIN SMALL LETTER S WITH CEDILLA */ }, \
+ { 0x015C, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ \
+ 0x015D, /* LATIN SMALL LETTER S WITH CIRCUMFLEX */ }, \
+ { 0x0218, /* LATIN CAPITAL LETTER S WITH COMMA BELOW */ \
+ 0x0219, /* LATIN SMALL LETTER S WITH COMMA BELOW */ }, \
+ { 0x1E60, /* LATIN CAPITAL LETTER S WITH DOT ABOVE */ \
+ 0x1E61, /* LATIN SMALL LETTER S WITH DOT ABOVE */ }, \
+ { 0x1E62, /* LATIN CAPITAL LETTER S WITH DOT BELOW */ \
+ 0x1E63, /* LATIN SMALL LETTER S WITH DOT BELOW */ }, \
+ { 0x1E68, /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */ \
+ 0x1E69, /* LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE */ }, \
+ { 0xA7A8, /* LATIN CAPITAL LETTER S WITH OBLIQUE STROKE */ \
+ 0xA7A9, /* LATIN SMALL LETTER S WITH OBLIQUE STROKE */ }, \
+ { 0x2C7E, /* LATIN CAPITAL LETTER S WITH SWASH TAIL */ \
+ 0x023F, /* LATIN SMALL LETTER S WITH SWASH TAIL */ }, \
+ { 0xA78B, /* LATIN CAPITAL LETTER SALTILLO */ \
+ 0xA78C, /* LATIN SMALL LETTER SALTILLO */ }, \
+ { 0x018F, /* LATIN CAPITAL LETTER SCHWA */ \
+ 0x0259, /* LATIN SMALL LETTER SCHWA */ }, \
+ { 0xA7AC, /* LATIN CAPITAL LETTER SCRIPT G */ \
+ 0x0261, /* LATIN SMALL LETTER SCRIPT G */ }, \
+ { 0x1E9E, /* LATIN CAPITAL LETTER SHARP S */ \
+ 0x00DF, /* LATIN SMALL LETTER SHARP S */ }, \
+ { 0x0054, /* LATIN CAPITAL LETTER T */ \
+ 0x0074, /* LATIN SMALL LETTER T */ }, \
+ { 0x0164, /* LATIN CAPITAL LETTER T WITH CARON */ \
+ 0x0165, /* LATIN SMALL LETTER T WITH CARON */ }, \
+ { 0x0162, /* LATIN CAPITAL LETTER T WITH CEDILLA */ \
+ 0x0163, /* LATIN SMALL LETTER T WITH CEDILLA */ }, \
+ { 0x1E70, /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */ \
+ 0x1E71, /* LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW */ }, \
+ { 0x021A, /* LATIN CAPITAL LETTER T WITH COMMA BELOW */ \
+ 0x021B, /* LATIN SMALL LETTER T WITH COMMA BELOW */ }, \
+ { 0x023E, /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE */ \
+ 0x2C66, /* LATIN SMALL LETTER T WITH DIAGONAL STROKE */ }, \
+ { 0x1E6A, /* LATIN CAPITAL LETTER T WITH DOT ABOVE */ \
+ 0x1E6B, /* LATIN SMALL LETTER T WITH DOT ABOVE */ }, \
+ { 0x1E6C, /* LATIN CAPITAL LETTER T WITH DOT BELOW */ \
+ 0x1E6D, /* LATIN SMALL LETTER T WITH DOT BELOW */ }, \
+ { 0x01AC, /* LATIN CAPITAL LETTER T WITH HOOK */ \
+ 0x01AD, /* LATIN SMALL LETTER T WITH HOOK */ }, \
+ { 0x1E6E, /* LATIN CAPITAL LETTER T WITH LINE BELOW */ \
+ 0x1E6F, /* LATIN SMALL LETTER T WITH LINE BELOW */ }, \
+ { 0x01AE, /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */ \
+ 0x0288, /* LATIN SMALL LETTER T WITH RETROFLEX HOOK */ }, \
+ { 0x0166, /* LATIN CAPITAL LETTER T WITH STROKE */ \
+ 0x0167, /* LATIN SMALL LETTER T WITH STROKE */ }, \
+ { 0x00DE, /* LATIN CAPITAL LETTER THORN */ \
+ 0x00FE, /* LATIN SMALL LETTER THORN */ }, \
+ { 0xA764, /* LATIN CAPITAL LETTER THORN WITH STROKE */ \
+ 0xA765, /* LATIN SMALL LETTER THORN WITH STROKE */ }, \
+ { 0xA766, /* LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER */ \
+ 0xA767, /* LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER */ }, \
+ { 0x01BC, /* LATIN CAPITAL LETTER TONE FIVE */ \
+ 0x01BD, /* LATIN SMALL LETTER TONE FIVE */ }, \
+ { 0x0184, /* LATIN CAPITAL LETTER TONE SIX */ \
+ 0x0185, /* LATIN SMALL LETTER TONE SIX */ }, \
+ { 0x01A7, /* LATIN CAPITAL LETTER TONE TWO */ \
+ 0x01A8, /* LATIN SMALL LETTER TONE TWO */ }, \
+ { 0xA72A, /* LATIN CAPITAL LETTER TRESILLO */ \
+ 0xA72B, /* LATIN SMALL LETTER TRESILLO */ }, \
+ { 0x2C6F, /* LATIN CAPITAL LETTER TURNED A */ \
+ 0x0250, /* LATIN SMALL LETTER TURNED A */ }, \
+ { 0x2C70, /* LATIN CAPITAL LETTER TURNED ALPHA */ \
+ 0x0252, /* LATIN SMALL LETTER TURNED ALPHA */ }, \
+ { 0xA78D, /* LATIN CAPITAL LETTER TURNED H */ \
+ 0x0265, /* LATIN SMALL LETTER TURNED H */ }, \
+ { 0xA77E, /* LATIN CAPITAL LETTER TURNED INSULAR G */ \
+ 0xA77F, /* LATIN SMALL LETTER TURNED INSULAR G */ }, \
+ { 0xA7B0, /* LATIN CAPITAL LETTER TURNED K */ \
+ 0x029E, /* LATIN SMALL LETTER TURNED K */ }, \
+ { 0xA780, /* LATIN CAPITAL LETTER TURNED L */ \
+ 0xA781, /* LATIN SMALL LETTER TURNED L */ }, \
+ { 0x019C, /* LATIN CAPITAL LETTER TURNED M */ \
+ 0x026F, /* LATIN SMALL LETTER TURNED M */ }, \
+ { 0xA7B1, /* LATIN CAPITAL LETTER TURNED T */ \
+ 0x0287, /* LATIN SMALL LETTER TURNED T */ }, \
+ { 0x0245, /* LATIN CAPITAL LETTER TURNED V */ \
+ 0x028C, /* LATIN SMALL LETTER TURNED V */ }, \
+ { 0xA728, /* LATIN CAPITAL LETTER TZ */ \
+ 0xA729, /* LATIN SMALL LETTER TZ */ }, \
+ { 0x0055, /* LATIN CAPITAL LETTER U */ \
+ 0x0075, /* LATIN SMALL LETTER U */ }, \
+ { 0x0244, /* LATIN CAPITAL LETTER U BAR */ \
+ 0x0289, /* LATIN SMALL LETTER U BAR */ }, \
+ { 0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ \
+ 0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ }, \
+ { 0x016C, /* LATIN CAPITAL LETTER U WITH BREVE */ \
+ 0x016D, /* LATIN SMALL LETTER U WITH BREVE */ }, \
+ { 0x01D3, /* LATIN CAPITAL LETTER U WITH CARON */ \
+ 0x01D4, /* LATIN SMALL LETTER U WITH CARON */ }, \
+ { 0x00DB, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ \
+ 0x00FB, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ }, \
+ { 0x1E76, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */ \
+ 0x1E77, /* LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW */ }, \
+ { 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x01D7, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */ \
+ 0x01D8, /* LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE */ }, \
+ { 0x01D9, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */ \
+ 0x01DA, /* LATIN SMALL LETTER U WITH DIAERESIS AND CARON */ }, \
+ { 0x01DB, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */ \
+ 0x01DC, /* LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE */ }, \
+ { 0x01D5, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */ \
+ 0x01D6, /* LATIN SMALL LETTER U WITH DIAERESIS AND MACRON */ }, \
+ { 0x1E72, /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */ \
+ 0x1E73, /* LATIN SMALL LETTER U WITH DIAERESIS BELOW */ }, \
+ { 0x1EE4, /* LATIN CAPITAL LETTER U WITH DOT BELOW */ \
+ 0x1EE5, /* LATIN SMALL LETTER U WITH DOT BELOW */ }, \
+ { 0x0170, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ \
+ 0x0171, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */ }, \
+ { 0x0214, /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */ \
+ 0x0215, /* LATIN SMALL LETTER U WITH DOUBLE GRAVE */ }, \
+ { 0x00D9, /* LATIN CAPITAL LETTER U WITH GRAVE */ \
+ 0x00F9, /* LATIN SMALL LETTER U WITH GRAVE */ }, \
+ { 0x1EE6, /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */ \
+ 0x1EE7, /* LATIN SMALL LETTER U WITH HOOK ABOVE */ }, \
+ { 0x01AF, /* LATIN CAPITAL LETTER U WITH HORN */ \
+ 0x01B0, /* LATIN SMALL LETTER U WITH HORN */ }, \
+ { 0x1EE8, /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */ \
+ 0x1EE9, /* LATIN SMALL LETTER U WITH HORN AND ACUTE */ }, \
+ { 0x1EF0, /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */ \
+ 0x1EF1, /* LATIN SMALL LETTER U WITH HORN AND DOT BELOW */ }, \
+ { 0x1EEA, /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */ \
+ 0x1EEB, /* LATIN SMALL LETTER U WITH HORN AND GRAVE */ }, \
+ { 0x1EEC, /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */ \
+ 0x1EED, /* LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE */ }, \
+ { 0x1EEE, /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */ \
+ 0x1EEF, /* LATIN SMALL LETTER U WITH HORN AND TILDE */ }, \
+ { 0x0216, /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */ \
+ 0x0217, /* LATIN SMALL LETTER U WITH INVERTED BREVE */ }, \
+ { 0x016A, /* LATIN CAPITAL LETTER U WITH MACRON */ \
+ 0x016B, /* LATIN SMALL LETTER U WITH MACRON */ }, \
+ { 0x1E7A, /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */ \
+ 0x1E7B, /* LATIN SMALL LETTER U WITH MACRON AND DIAERESIS */ }, \
+ { 0x0172, /* LATIN CAPITAL LETTER U WITH OGONEK */ \
+ 0x0173, /* LATIN SMALL LETTER U WITH OGONEK */ }, \
+ { 0x016E, /* LATIN CAPITAL LETTER U WITH RING ABOVE */ \
+ 0x016F, /* LATIN SMALL LETTER U WITH RING ABOVE */ }, \
+ { 0xA7B8, /* LATIN CAPITAL LETTER U WITH STROKE */ \
+ 0xA7B9, /* LATIN SMALL LETTER U WITH STROKE */ }, \
+ { 0x0168, /* LATIN CAPITAL LETTER U WITH TILDE */ \
+ 0x0169, /* LATIN SMALL LETTER U WITH TILDE */ }, \
+ { 0x1E78, /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */ \
+ 0x1E79, /* LATIN SMALL LETTER U WITH TILDE AND ACUTE */ }, \
+ { 0x1E74, /* LATIN CAPITAL LETTER U WITH TILDE BELOW */ \
+ 0x1E75, /* LATIN SMALL LETTER U WITH TILDE BELOW */ }, \
+ { 0x01B1, /* LATIN CAPITAL LETTER UPSILON */ \
+ 0x028A, /* LATIN SMALL LETTER UPSILON */ }, \
+ { 0x0056, /* LATIN CAPITAL LETTER V */ \
+ 0x0076, /* LATIN SMALL LETTER V */ }, \
+ { 0xA75E, /* LATIN CAPITAL LETTER V WITH DIAGONAL STROKE */ \
+ 0xA75F, /* LATIN SMALL LETTER V WITH DIAGONAL STROKE */ }, \
+ { 0x1E7E, /* LATIN CAPITAL LETTER V WITH DOT BELOW */ \
+ 0x1E7F, /* LATIN SMALL LETTER V WITH DOT BELOW */ }, \
+ { 0x01B2, /* LATIN CAPITAL LETTER V WITH HOOK */ \
+ 0x028B, /* LATIN SMALL LETTER V WITH HOOK */ }, \
+ { 0x1E7C, /* LATIN CAPITAL LETTER V WITH TILDE */ \
+ 0x1E7D, /* LATIN SMALL LETTER V WITH TILDE */ }, \
+ { 0xA768, /* LATIN CAPITAL LETTER VEND */ \
+ 0xA769, /* LATIN SMALL LETTER VEND */ }, \
+ { 0xA762, /* LATIN CAPITAL LETTER VISIGOTHIC Z */ \
+ 0xA763, /* LATIN SMALL LETTER VISIGOTHIC Z */ }, \
+ { 0xA79A, /* LATIN CAPITAL LETTER VOLAPUK AE */ \
+ 0xA79B, /* LATIN SMALL LETTER VOLAPUK AE */ }, \
+ { 0xA79C, /* LATIN CAPITAL LETTER VOLAPUK OE */ \
+ 0xA79D, /* LATIN SMALL LETTER VOLAPUK OE */ }, \
+ { 0xA79E, /* LATIN CAPITAL LETTER VOLAPUK UE */ \
+ 0xA79F, /* LATIN SMALL LETTER VOLAPUK UE */ }, \
+ { 0xA760, /* LATIN CAPITAL LETTER VY */ \
+ 0xA761, /* LATIN SMALL LETTER VY */ }, \
+ { 0x0057, /* LATIN CAPITAL LETTER W */ \
+ 0x0077, /* LATIN SMALL LETTER W */ }, \
+ { 0x1E82, /* LATIN CAPITAL LETTER W WITH ACUTE */ \
+ 0x1E83, /* LATIN SMALL LETTER W WITH ACUTE */ }, \
+ { 0x0174, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ \
+ 0x0175, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */ }, \
+ { 0x1E84, /* LATIN CAPITAL LETTER W WITH DIAERESIS */ \
+ 0x1E85, /* LATIN SMALL LETTER W WITH DIAERESIS */ }, \
+ { 0x1E86, /* LATIN CAPITAL LETTER W WITH DOT ABOVE */ \
+ 0x1E87, /* LATIN SMALL LETTER W WITH DOT ABOVE */ }, \
+ { 0x1E88, /* LATIN CAPITAL LETTER W WITH DOT BELOW */ \
+ 0x1E89, /* LATIN SMALL LETTER W WITH DOT BELOW */ }, \
+ { 0x1E80, /* LATIN CAPITAL LETTER W WITH GRAVE */ \
+ 0x1E81, /* LATIN SMALL LETTER W WITH GRAVE */ }, \
+ { 0x2C72, /* LATIN CAPITAL LETTER W WITH HOOK */ \
+ 0x2C73, /* LATIN SMALL LETTER W WITH HOOK */ }, \
+ { 0x0058, /* LATIN CAPITAL LETTER X */ \
+ 0x0078, /* LATIN SMALL LETTER X */ }, \
+ { 0x1E8C, /* LATIN CAPITAL LETTER X WITH DIAERESIS */ \
+ 0x1E8D, /* LATIN SMALL LETTER X WITH DIAERESIS */ }, \
+ { 0x1E8A, /* LATIN CAPITAL LETTER X WITH DOT ABOVE */ \
+ 0x1E8B, /* LATIN SMALL LETTER X WITH DOT ABOVE */ }, \
+ { 0x0059, /* LATIN CAPITAL LETTER Y */ \
+ 0x0079, /* LATIN SMALL LETTER Y */ }, \
+ { 0x00DD, /* LATIN CAPITAL LETTER Y WITH ACUTE */ \
+ 0x00FD, /* LATIN SMALL LETTER Y WITH ACUTE */ }, \
+ { 0x0176, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ \
+ 0x0177, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */ }, \
+ { 0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ \
+ 0x00FF, /* LATIN SMALL LETTER Y WITH DIAERESIS */ }, \
+ { 0x1E8E, /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */ \
+ 0x1E8F, /* LATIN SMALL LETTER Y WITH DOT ABOVE */ }, \
+ { 0x1EF4, /* LATIN CAPITAL LETTER Y WITH DOT BELOW */ \
+ 0x1EF5, /* LATIN SMALL LETTER Y WITH DOT BELOW */ }, \
+ { 0x1EF2, /* LATIN CAPITAL LETTER Y WITH GRAVE */ \
+ 0x1EF3, /* LATIN SMALL LETTER Y WITH GRAVE */ }, \
+ { 0x01B3, /* LATIN CAPITAL LETTER Y WITH HOOK */ \
+ 0x01B4, /* LATIN SMALL LETTER Y WITH HOOK */ }, \
+ { 0x1EF6, /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ \
+ 0x1EF7, /* LATIN SMALL LETTER Y WITH HOOK ABOVE */ }, \
+ { 0x1EFE, /* LATIN CAPITAL LETTER Y WITH LOOP */ \
+ 0x1EFF, /* LATIN SMALL LETTER Y WITH LOOP */ }, \
+ { 0x0232, /* LATIN CAPITAL LETTER Y WITH MACRON */ \
+ 0x0233, /* LATIN SMALL LETTER Y WITH MACRON */ }, \
+ { 0x024E, /* LATIN CAPITAL LETTER Y WITH STROKE */ \
+ 0x024F, /* LATIN SMALL LETTER Y WITH STROKE */ }, \
+ { 0x1EF8, /* LATIN CAPITAL LETTER Y WITH TILDE */ \
+ 0x1EF9, /* LATIN SMALL LETTER Y WITH TILDE */ }, \
+ { 0x021C, /* LATIN CAPITAL LETTER YOGH */ \
+ 0x021D, /* LATIN SMALL LETTER YOGH */ }, \
+ { 0x005A, /* LATIN CAPITAL LETTER Z */ \
+ 0x007A, /* LATIN SMALL LETTER Z */ }, \
+ { 0x0179, /* LATIN CAPITAL LETTER Z WITH ACUTE */ \
+ 0x017A, /* LATIN SMALL LETTER Z WITH ACUTE */ }, \
+ { 0x017D, /* LATIN CAPITAL LETTER Z WITH CARON */ \
+ 0x017E, /* LATIN SMALL LETTER Z WITH CARON */ }, \
+ { 0x1E90, /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ \
+ 0x1E91, /* LATIN SMALL LETTER Z WITH CIRCUMFLEX */ }, \
+ { 0x2C6B, /* LATIN CAPITAL LETTER Z WITH DESCENDER */ \
+ 0x2C6C, /* LATIN SMALL LETTER Z WITH DESCENDER */ }, \
+ { 0x017B, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ \
+ 0x017C, /* LATIN SMALL LETTER Z WITH DOT ABOVE */ }, \
+ { 0x1E92, /* LATIN CAPITAL LETTER Z WITH DOT BELOW */ \
+ 0x1E93, /* LATIN SMALL LETTER Z WITH DOT BELOW */ }, \
+ { 0x0224, /* LATIN CAPITAL LETTER Z WITH HOOK */ \
+ 0x0225, /* LATIN SMALL LETTER Z WITH HOOK */ }, \
+ { 0x1E94, /* LATIN CAPITAL LETTER Z WITH LINE BELOW */ \
+ 0x1E95, /* LATIN SMALL LETTER Z WITH LINE BELOW */ }, \
+ { 0x01B5, /* LATIN CAPITAL LETTER Z WITH STROKE */ \
+ 0x01B6, /* LATIN SMALL LETTER Z WITH STROKE */ }, \
+ { 0x2C7F, /* LATIN CAPITAL LETTER Z WITH SWASH TAIL */ \
+ 0x0240, /* LATIN SMALL LETTER Z WITH SWASH TAIL */ }, \
+ { 0x0000, /* END OF LIST CAPITAL LETTERS */ \
+ 0x0000, /* END OF LIST SMALL LETTERS */ }, \
+}
+
+/*
+ * Correspondence table for small and capital letters of codepage 437.
+ * Letters A-Z are handled in code.
+ */
+#define CP437_CAPITALIZATION_TABLE { \
+ { 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ \
+ 0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ }, \
+ { 0x00C6, /* LATIN CAPITAL LETTER AE */ \
+ 0x00E6, /* LATIN SMALL LETTER AE */ }, \
+ { 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ \
+ 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ }, \
+ { 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ \
+ 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ }, \
+ { 0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ \
+ 0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ }, \
+ { 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x03A3, /* GREEK CAPITAL LETTER SIGMA */ \
+ 0x03C3, /* GREEK SMALL LETTER SIGMA */ }, \
+ { 0x03A6, /* GREEK CAPITAL LETTER PHI */ \
+ 0x03C6, /* GREEK SMALL LETTER PHI */ }, \
+ { 0x0000, 0x0000, }, \
+}
+
+/*
+ * Correspondence table for small and capital letters of codepage 1250.
+ * Letters A-Z are handled in code.
+ */
+#define CP1250_CAPITALIZATION_TABLE { \
+ { 0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ \
+ 0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ }, \
+ { 0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ \
+ 0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ }, \
+ { 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ \
+ 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ }, \
+ { 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ \
+ 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ }, \
+ { 0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ \
+ 0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ }, \
+ { 0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ \
+ 0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ }, \
+ { 0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ \
+ 0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ }, \
+ { 0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ \
+ 0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ }, \
+ { 0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ \
+ 0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ }, \
+ { 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ \
+ 0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ }, \
+ { 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x00DD, /* LATIN CAPITAL LETTER Y WITH ACUTE */ \
+ 0x00FD, /* LATIN SMALL LETTER Y WITH ACUTE */ }, \
+ { 0x0102, /* LATIN CAPITAL LETTER A WITH BREVE */ \
+ 0x0103, /* LATIN SMALL LETTER A WITH BREVE */ }, \
+ { 0x0104, /* LATIN CAPITAL LETTER A WITH OGONEK */ \
+ 0x0105, /* LATIN SMALL LETTER A WITH OGONEK */ }, \
+ { 0x0106, /* LATIN CAPITAL LETTER C WITH ACUTE */ \
+ 0x0107, /* LATIN SMALL LETTER C WITH ACUTE */ }, \
+ { 0x010C, /* LATIN CAPITAL LETTER C WITH CARON */ \
+ 0x010D, /* LATIN SMALL LETTER C WITH CARON */ }, \
+ { 0x010E, /* LATIN CAPITAL LETTER D WITH CARON */ \
+ 0x010F, /* LATIN SMALL LETTER D WITH CARON */ }, \
+ { 0x0110, /* LATIN CAPITAL LETTER D WITH STROKE */ \
+ 0x0111, /* LATIN SMALL LETTER D WITH STROKE */ }, \
+ { 0x0118, /* LATIN CAPITAL LETTER E WITH OGONEK */ \
+ 0x0119, /* LATIN SMALL LETTER E WITH OGONEK */ }, \
+ { 0x011A, /* LATIN CAPITAL LETTER E WITH CARON */ \
+ 0x011B, /* LATIN SMALL LETTER E WITH CARON */ }, \
+ { 0x0139, /* LATIN CAPITAL LETTER L WITH ACUTE */ \
+ 0x013A, /* LATIN SMALL LETTER L WITH ACUTE */ }, \
+ { 0x013D, /* LATIN CAPITAL LETTER L WITH CARON */ \
+ 0x013E, /* LATIN SMALL LETTER L WITH CARON */ }, \
+ { 0x0141, /* LATIN CAPITAL LETTER L WITH STROKE */ \
+ 0x0142, /* LATIN SMALL LETTER L WITH STROKE */ }, \
+ { 0x0143, /* LATIN CAPITAL LETTER N WITH ACUTE */ \
+ 0x0144, /* LATIN SMALL LETTER N WITH ACUTE */ }, \
+ { 0x0147, /* LATIN CAPITAL LETTER N WITH CARON */ \
+ 0x0148, /* LATIN SMALL LETTER N WITH CARON */ }, \
+ { 0x0150, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ \
+ 0x0151, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */ }, \
+ { 0x0154, /* LATIN CAPITAL LETTER R WITH ACUTE */ \
+ 0x0155, /* LATIN SMALL LETTER R WITH ACUTE */ }, \
+ { 0x0158, /* LATIN CAPITAL LETTER R WITH CARON */ \
+ 0x0159, /* LATIN SMALL LETTER R WITH CARON */ }, \
+ { 0x015A, /* LATIN CAPITAL LETTER S WITH ACUTE */ \
+ 0x015B, /* LATIN SMALL LETTER S WITH ACUTE */ }, \
+ { 0x015E, /* LATIN CAPITAL LETTER S WITH CEDILLA */ \
+ 0x015F, /* LATIN SMALL LETTER S WITH CEDILLA */ }, \
+ { 0x0160, /* LATIN CAPITAL LETTER S WITH CARON */ \
+ 0x0161, /* LATIN SMALL LETTER S WITH CARON */ }, \
+ { 0x0162, /* LATIN CAPITAL LETTER T WITH CEDILLA */ \
+ 0x0163, /* LATIN SMALL LETTER T WITH CEDILLA */ }, \
+ { 0x0164, /* LATIN CAPITAL LETTER T WITH CARON */ \
+ 0x0165, /* LATIN SMALL LETTER T WITH CARON */ }, \
+ { 0x016E, /* LATIN CAPITAL LETTER U WITH RING ABOVE */ \
+ 0x016F, /* LATIN SMALL LETTER U WITH RING ABOVE */ }, \
+ { 0x0170, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ \
+ 0x0171, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */ }, \
+ { 0x0179, /* LATIN CAPITAL LETTER Z WITH ACUTE */ \
+ 0x017A, /* LATIN SMALL LETTER Z WITH ACUTE */ }, \
+ { 0x017B, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ \
+ 0x017C, /* LATIN SMALL LETTER Z WITH DOT ABOVE */ }, \
+ { 0x017D, /* LATIN CAPITAL LETTER Z WITH CARON */ \
+ 0x017E, /* LATIN SMALL LETTER Z WITH CARON */ }, \
+ { 0x0000, 0x0000, }, \
+}
diff --git a/include/charset.h b/include/charset.h
index 11832cb..4d45e24 100644
--- a/include/charset.h
+++ b/include/charset.h
@@ -8,44 +8,188 @@
#ifndef __CHARSET_H_
#define __CHARSET_H_
+#include <linux/kernel.h>
#include <linux/types.h>
#define MAX_UTF8_PER_UTF16 3
/**
- * utf16_strlen() - Get the length of an utf16 string
+ * console_read_unicode() - read Unicode code point from console
*
- * Returns the number of 16 bit characters in an utf16 string, not
- * including the terminating NULL character.
+ * @code: pointer to store Unicode code point
+ * Return: 0 = success
+ */
+int console_read_unicode(s32 *code);
+
+/**
+ * utf8_get() - get next UTF-8 code point from buffer
+ *
+ * @src: pointer to current byte, updated to point to next byte
+ * Return: code point, or 0 for end of string, or -1 if no legal
+ * code point is found. In case of an error src points to
+ * the incorrect byte.
+ */
+s32 utf8_get(const char **src);
+
+/**
+ * utf8_put() - write UTF-8 code point to buffer
+ *
+ * @code: code point
+ * @dst: pointer to destination buffer, updated to next position
+ * Return: -1 if the input parameters are invalid
+ */
+int utf8_put(s32 code, char **dst);
+
+/**
+ * utf8_utf16_strnlen() - length of a truncated utf-8 string after conversion
+ * to utf-16
+ *
+ * @src: utf-8 string
+ * @count: maximum number of code points to convert
+ * Return: length in bytes after conversion to utf-16 without the
+ * trailing \0. If an invalid UTF-8 sequence is hit one
+ * word will be reserved for a replacement character.
+ */
+size_t utf8_utf16_strnlen(const char *src, size_t count);
+
+/**
+ * utf8_utf16_strlen() - length of a utf-8 string after conversion to utf-16
+ *
+ * @src: utf-8 string
+ * Return: length in bytes after conversion to utf-16 without the
+ * trailing \0. -1 if the utf-8 string is not valid.
+ */
+#define utf8_utf16_strlen(a) utf8_utf16_strnlen((a), SIZE_MAX)
+
+/**
+ * utf8_utf16_strncpy() - copy utf-8 string to utf-16 string
+ *
+ * @dst: destination buffer
+ * @src: source buffer
+ * @count: maximum number of code points to copy
+ * Return: -1 if the input parameters are invalid
+ */
+int utf8_utf16_strncpy(u16 **dst, const char *src, size_t count);
+
+/**
+ * utf8_utf16_strcpy() - copy utf-8 string to utf-16 string
+ *
+ * @dst: destination buffer
+ * @src: source buffer
+ * Return: -1 if the input parameters are invalid
+ */
+#define utf8_utf16_strcpy(d, s) utf8_utf16_strncpy((d), (s), SIZE_MAX)
+
+/**
+ * utf16_get() - get next UTF-16 code point from buffer
+ *
+ * @src: pointer to current word, updated to point to next word
+ * Return: code point, or 0 for end of string, or -1 if no legal
+ * code point is found. In case of an error src points to
+ * the incorrect word.
+ */
+s32 utf16_get(const u16 **src);
+
+/**
+ * utf16_put() - write UTF-16 code point to buffer
+ *
+ * @code: code point
+ * @dst: pointer to destination buffer, updated to next position
+ * Return: -1 if the input parameters are invalid
+ */
+int utf16_put(s32 code, u16 **dst);
+
+/**
+ * utf16_strnlen() - length of a truncated utf-16 string
*
- * @in the string to measure
- * @return the string length
+ * @src: utf-16 string
+ * @count: maximum number of code points to convert
+ * Return: length in code points. If an invalid UTF-16 sequence is
+ * hit one position will be reserved for a replacement
+ * character.
*/
-size_t utf16_strlen(const uint16_t *in);
+size_t utf16_strnlen(const u16 *src, size_t count);
/**
- * utf16_strnlen() - Get the length of a fixed-size utf16 string.
+ * utf16_utf8_strnlen() - length of a truncated utf-16 string after conversion
+ * to utf-8
*
- * Returns the number of 16 bit characters in an utf16 string,
- * not including the terminating NULL character, but at most
- * 'count' number of characters. In doing this, utf16_strnlen()
- * looks at only the first 'count' characters.
+ * @src: utf-16 string
+ * @count: maximum number of code points to convert
+ * Return: length in bytes after conversion to utf-8 without the
+ * trailing \0. If an invalid UTF-16 sequence is hit one
+ * byte will be reserved for a replacement character.
+ */
+size_t utf16_utf8_strnlen(const u16 *src, size_t count);
+
+/**
+ * utf16_utf8_strlen() - length of a utf-16 string after conversion to utf-8
*
- * @in the string to measure
- * @count the maximum number of characters to count
- * @return the string length, up to a maximum of 'count'
+ * @src: utf-16 string
+ * Return: length in bytes after conversion to utf-8 without the
+ * trailing \0. -1 if the utf-16 string is not valid.
*/
-size_t utf16_strnlen(const uint16_t *in, size_t count);
+#define utf16_utf8_strlen(a) utf16_utf8_strnlen((a), SIZE_MAX)
/**
- * utf16_strcpy() - UTF16 equivalent of strcpy()
+ * utf16_utf8_strncpy() - copy utf-16 string to utf-8 string
+ *
+ * @dst: destination buffer
+ * @src: source buffer
+ * @count: maximum number of code points to copy
+ * Return: -1 if the input parameters are invalid
*/
-uint16_t *utf16_strcpy(uint16_t *dest, const uint16_t *src);
+int utf16_utf8_strncpy(char **dst, const u16 *src, size_t count);
/**
- * utf16_strdup() - UTF16 equivalent of strdup()
+ * utf16_utf8_strcpy() - copy utf-16 string to utf-8 string
+ *
+ * @dst: destination buffer
+ * @src: source buffer
+ * Return: -1 if the input parameters are invalid
*/
-uint16_t *utf16_strdup(const uint16_t *s);
+#define utf16_utf8_strcpy(d, s) utf16_utf8_strncpy((d), (s), SIZE_MAX)
+
+/**
+ * utf_to_lower() - convert a Unicode letter to lower case
+ *
+ * @code: letter to convert
+ * Return: lower case letter or unchanged letter
+ */
+s32 utf_to_lower(const s32 code);
+
+/**
+ * utf_to_upper() - convert a Unicode letter to upper case
+ *
+ * @code: letter to convert
+ * Return: upper case letter or unchanged letter
+ */
+s32 utf_to_upper(const s32 code);
+
+/**
+ * u16_strlen - count non-zero words
+ *
+ * This function matches wsclen() if the -fshort-wchar compiler flag is set.
+ * In the EFI context we explicitly need a function handling u16 strings.
+ *
+ * @in: null terminated u16 string
+ * ReturnValue: number of non-zero words.
+ * This is not the number of utf-16 letters!
+ */
+size_t u16_strlen(const u16 *in);
+
+/**
+ * u16_strlen - count non-zero words
+ *
+ * This function matches wscnlen_s() if the -fshort-wchar compiler flag is set.
+ * In the EFI context we explicitly need a function handling u16 strings.
+ *
+ * @in: null terminated u16 string
+ * @count: maximum number of words to count
+ * ReturnValue: number of non-zero words.
+ * This is not the number of utf-16 letters!
+ */
+size_t u16_strnlen(const u16 *in, size_t count);
/**
* utf16_to_utf8() - Convert an utf16 string to utf8
@@ -63,17 +207,4 @@ uint16_t *utf16_strdup(const uint16_t *s);
*/
uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
-/**
- * utf8_to_utf16() - Convert an utf8 string to utf16
- *
- * Converts up to 'size' characters of the utf16 string 'src' to utf8
- * written to the 'dest' buffer. Stops at 0x00.
- *
- * @dest the destination buffer to write the utf8 characters
- * @src the source utf16 string
- * @size maximum number of utf16 characters to convert
- * @return the pointer to the first unwritten byte in 'dest'
- */
-uint16_t *utf8_to_utf16(uint16_t *dest, const uint8_t *src, size_t size);
-
#endif /* __CHARSET_H_ */
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
index d672e8e..373fee7 100644
--- a/include/config_distro_bootcmd.h
+++ b/include/config_distro_bootcmd.h
@@ -245,22 +245,26 @@
#if defined(CONFIG_CMD_DHCP)
#if defined(CONFIG_EFI_LOADER)
/* http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml */
-#if defined(CONFIG_ARM64)
+#if defined(CONFIG_ARM64) || defined(__aarch64__)
#define BOOTENV_EFI_PXE_ARCH "0xb"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00011:UNDI:003000"
-#elif defined(CONFIG_ARM)
+#elif defined(CONFIG_ARM) || defined(__arm__)
#define BOOTENV_EFI_PXE_ARCH "0xa"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00010:UNDI:003000"
-#elif defined(CONFIG_X86)
-/* Always assume we're running 64bit */
+#elif defined(CONFIG_X86) || defined(__x86_64__)
#define BOOTENV_EFI_PXE_ARCH "0x7"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00007:UNDI:003000"
-#elif defined(CONFIG_CPU_RISCV_32)
+#elif defined(__i386__)
+#define BOOTENV_EFI_PXE_ARCH "0x6"
+#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00006:UNDI:003000"
+#elif defined(CONFIG_CPU_RISCV_32) || ((defined(__riscv) && __riscv_xlen == 32))
#define BOOTENV_EFI_PXE_ARCH "0x19"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00025:UNDI:003000"
-#elif defined(CONFIG_CPU_RISCV_64)
+#elif defined(CONFIG_CPU_RISCV_64) || ((defined(__riscv) && __riscv_xlen == 64))
#define BOOTENV_EFI_PXE_ARCH "0x1b"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00027:UNDI:003000"
+#elif defined(CONFIG_SANDBOX)
+# error "sandbox EFI support is only supported on ARM and x86"
#else
#error Please specify an EFI client identifier
#endif
diff --git a/include/configs/qemu-arm.h b/include/configs/qemu-arm.h
index 5a12a32..fedc466 100644
--- a/include/configs/qemu-arm.h
+++ b/include/configs/qemu-arm.h
@@ -20,9 +20,6 @@
/* For timer, QEMU emulates an ARMv7/ARMv8 architected timer */
#define CONFIG_SYS_HZ 1000
-/* QEMU emulates the ARM AMBA PL031 RTC */
-#define CONFIG_SYS_RTC_PL031_BASE 0x09010000
-
/* Environment options */
#define CONFIG_ENV_SIZE SZ_64K
diff --git a/include/cp1250.h b/include/cp1250.h
new file mode 100644
index 0000000..adacf8a
--- /dev/null
+++ b/include/cp1250.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Constant CP1250 contains the Unicode code points for characters 0x80 - 0xff
+ * of the code page 1250.
+ */
+#define CP1250 { \
+ 0x20ac, 0x0000, 0x201a, 0x0000, \
+ 0x201e, 0x2026, 0x2020, 0x2021, \
+ 0x0000, 0x2030, 0x0160, 0x2039, \
+ 0x015a, 0x0164, 0x017d, 0x0179, \
+ 0x0000, 0x2018, 0x2019, 0x201c, \
+ 0x201d, 0x2022, 0x2013, 0x2014, \
+ 0x0000, 0x2122, 0x0161, 0x203a, \
+ 0x015b, 0x0165, 0x017e, 0x017a, \
+ 0x00a0, 0x02c7, 0x02d8, 0x0141, \
+ 0x00a4, 0x0104, 0x00a6, 0x00a7, \
+ 0x00a8, 0x00a9, 0x015e, 0x00ab, \
+ 0x00ac, 0x00ad, 0x00ae, 0x017b, \
+ 0x00b0, 0x00b1, 0x02db, 0x0142, \
+ 0x00b4, 0x00b5, 0x00b6, 0x00b7, \
+ 0x00b8, 0x0105, 0x015f, 0x00bb, \
+ 0x013d, 0x02dd, 0x013e, 0x017c, \
+ 0x0154, 0x00c1, 0x00c2, 0x0102, \
+ 0x00c4, 0x0139, 0x0106, 0x00c7, \
+ 0x010c, 0x00c9, 0x0118, 0x00cb, \
+ 0x011a, 0x00cd, 0x00ce, 0x010e, \
+ 0x0110, 0x0143, 0x0147, 0x00d3, \
+ 0x00d4, 0x0150, 0x00d6, 0x00d7, \
+ 0x0158, 0x016e, 0x00da, 0x0170, \
+ 0x00dc, 0x00dd, 0x0162, 0x00df, \
+ 0x0155, 0x00e1, 0x00e2, 0x0103, \
+ 0x00e4, 0x013a, 0x0107, 0x00e7, \
+ 0x010d, 0x00e9, 0x0119, 0x00eb, \
+ 0x011b, 0x00ed, 0x00ee, 0x010f, \
+ 0x0111, 0x0144, 0x0148, 0x00f3, \
+ 0x00f4, 0x0151, 0x00f6, 0x00f7, \
+ 0x0159, 0x016f, 0x00fa, 0x0171, \
+ 0x00fc, 0x00fd, 0x0163, 0x02d9, \
+}
diff --git a/include/cp437.h b/include/cp437.h
new file mode 100644
index 0000000..0b2b971
--- /dev/null
+++ b/include/cp437.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Constant CP437 contains the Unicode code points for characters 0x80 - 0xff
+ * of the code page 437.
+ */
+#define CP437 { \
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2, \
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7, \
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef, \
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5, \
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4, \
+ 0x00f6, 0x00f2, 0x00fb, 0x00f9, \
+ 0x00ff, 0x00d6, 0x00dc, 0x00a2, \
+ 0x00a3, 0x00a5, 0x20a7, 0x0192, \
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa, \
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba, \
+ 0x00bf, 0x2310, 0x00ac, 0x00bd, \
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb, \
+ 0x2591, 0x2592, 0x2593, 0x2502, \
+ 0x2524, 0x2561, 0x2562, 0x2556, \
+ 0x2555, 0x2563, 0x2551, 0x2557, \
+ 0x255d, 0x255c, 0x255b, 0x2510, \
+ 0x2514, 0x2534, 0x252c, 0x251c, \
+ 0x2500, 0x253c, 0x255e, 0x255f, \
+ 0x255a, 0x2554, 0x2569, 0x2566, \
+ 0x2560, 0x2550, 0x256c, 0x2567, \
+ 0x2568, 0x2564, 0x2565, 0x2559, \
+ 0x2558, 0x2552, 0x2553, 0x256b, \
+ 0x256a, 0x2518, 0x250c, 0x2588, \
+ 0x2584, 0x258c, 0x2590, 0x2580, \
+ 0x03b1, 0x00df, 0x0393, 0x03c0, \
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4, \
+ 0x03a6, 0x0398, 0x03a9, 0x03b4, \
+ 0x221e, 0x03c6, 0x03b5, 0x2229, \
+ 0x2261, 0x00b1, 0x2265, 0x2264, \
+ 0x2320, 0x2321, 0x00f7, 0x2248, \
+ 0x00b0, 0x2219, 0x00b7, 0x221a, \
+ 0x207f, 0x00b2, 0x25a0, 0x00a0, \
+}
diff --git a/include/efi.h b/include/efi.h
index e1854ec..b1deb60 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Extensible Firmware Interface
* Based on 'Extensible Firmware Interface Specification' version 0.9,
diff --git a/include/efi_api.h b/include/efi_api.h
index ebf2a3b..bea19a5 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Extensible Firmware Interface
* Based on 'Extensible Firmware Interface Specification' version 0.9,
@@ -31,6 +32,7 @@ enum efi_timer_delay {
EFI_TIMER_RELATIVE = 2
};
+#define efi_intn_t ssize_t
#define efi_uintn_t size_t
typedef uint16_t *efi_string_t;
@@ -294,8 +296,7 @@ struct efi_runtime_services {
EFI_GUID(0xeb9d2d31, 0x2d88, 0x11d3, \
0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
-struct efi_configuration_table
-{
+struct efi_configuration_table {
efi_guid_t guid;
void *table;
};
@@ -307,7 +308,7 @@ struct efi_system_table {
u16 *fw_vendor; /* physical addr of wchar_t vendor string */
u32 fw_revision;
efi_handle_t con_in_handle;
- struct efi_simple_input_interface *con_in;
+ struct efi_simple_text_input_protocol *con_in;
efi_handle_t con_out_handle;
struct efi_simple_text_output_protocol *con_out;
efi_handle_t stderr_handle;
@@ -338,19 +339,11 @@ struct efi_loaded_image {
unsigned int image_code_type;
unsigned int image_data_type;
unsigned long unload;
-
- /* Below are efi loader private fields */
-#ifdef CONFIG_EFI_LOADER
- void *reloc_base;
- aligned_u64 reloc_size;
- efi_status_t exit_status;
- struct jmp_buf_data exit_jmp;
-#endif
};
#define DEVICE_PATH_GUID \
EFI_GUID(0x09576e91, 0x6d3f, 0x11d2, \
- 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+ 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define DEVICE_PATH_TYPE_END 0x7f
# define DEVICE_PATH_SUB_TYPE_INSTANCE_END 0x01
@@ -475,8 +468,7 @@ struct efi_device_path_file_path {
EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \
0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
-struct efi_block_io_media
-{
+struct efi_block_io_media {
u32 media_id;
char removable_media;
char media_present;
@@ -521,7 +513,6 @@ struct simple_text_output_mode {
bool cursor_visible;
};
-
#define EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID \
EFI_GUID(0x387477c2, 0x69c7, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
@@ -588,20 +579,76 @@ struct efi_simple_text_output_protocol {
struct simple_text_output_mode *mode;
};
+#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
+ EFI_GUID(0xdd9e7534, 0x7762, 0x4698, \
+ 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa)
+
struct efi_input_key {
u16 scan_code;
s16 unicode_char;
};
+#define EFI_SHIFT_STATE_INVALID 0x00000000
+#define EFI_RIGHT_SHIFT_PRESSED 0x00000001
+#define EFI_LEFT_SHIFT_PRESSED 0x00000002
+#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
+#define EFI_LEFT_CONTROL_PRESSED 0x00000008
+#define EFI_RIGHT_ALT_PRESSED 0x00000010
+#define EFI_LEFT_ALT_PRESSED 0x00000020
+#define EFI_RIGHT_LOGO_PRESSED 0x00000040
+#define EFI_LEFT_LOGO_PRESSED 0x00000080
+#define EFI_MENU_KEY_PRESSED 0x00000100
+#define EFI_SYS_REQ_PRESSED 0x00000200
+#define EFI_SHIFT_STATE_VALID 0x80000000
+
+#define EFI_TOGGLE_STATE_INVALID 0x00
+#define EFI_SCROLL_LOCK_ACTIVE 0x01
+#define EFI_NUM_LOCK_ACTIVE 0x02
+#define EFI_CAPS_LOCK_ACTIVE 0x04
+#define EFI_KEY_STATE_EXPOSED 0x40
+#define EFI_TOGGLE_STATE_VALID 0x80
+
+struct efi_key_state {
+ u32 key_shift_state;
+ u8 key_toggle_state;
+};
+
+struct efi_key_data {
+ struct efi_input_key key;
+ struct efi_key_state key_state;
+};
+
+struct efi_simple_text_input_ex_protocol {
+ efi_status_t (EFIAPI *reset) (
+ struct efi_simple_text_input_ex_protocol *this,
+ bool extended_verification);
+ efi_status_t (EFIAPI *read_key_stroke_ex) (
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data);
+ struct efi_event *wait_for_key_ex;
+ efi_status_t (EFIAPI *set_state) (
+ struct efi_simple_text_input_ex_protocol *this,
+ u8 key_toggle_state);
+ efi_status_t (EFIAPI *register_key_notify) (
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data,
+ efi_status_t (EFIAPI *key_notify_function)(
+ struct efi_key_data *key_data),
+ void **notify_handle);
+ efi_status_t (EFIAPI *unregister_key_notify) (
+ struct efi_simple_text_input_ex_protocol *this,
+ void *notification_handle);
+};
+
#define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \
EFI_GUID(0x387477c1, 0x69c7, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
-struct efi_simple_input_interface {
- efi_status_t(EFIAPI *reset)(struct efi_simple_input_interface *this,
- bool ExtendedVerification);
+struct efi_simple_text_input_protocol {
+ efi_status_t(EFIAPI *reset)(struct efi_simple_text_input_protocol *this,
+ bool extended_verification);
efi_status_t(EFIAPI *read_key_stroke)(
- struct efi_simple_input_interface *this,
+ struct efi_simple_text_input_protocol *this,
struct efi_input_key *key);
struct efi_event *wait_for_key;
};
@@ -610,8 +657,7 @@ struct efi_simple_input_interface {
EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
-struct efi_device_path_to_text_protocol
-{
+struct efi_device_path_to_text_protocol {
uint16_t *(EFIAPI *convert_device_node_to_text)(
struct efi_device_path *device_node,
bool display_only,
@@ -659,8 +705,7 @@ struct efi_device_path_utilities_protocol {
#define EFI_GOT_BGRA8 1
#define EFI_GOT_BITMASK 2
-struct efi_gop_mode_info
-{
+struct efi_gop_mode_info {
u32 version;
u32 width;
u32 height;
@@ -669,8 +714,7 @@ struct efi_gop_mode_info
u32 pixels_per_scanline;
};
-struct efi_gop_mode
-{
+struct efi_gop_mode {
u32 max_mode;
u32 mode;
struct efi_gop_mode_info *info;
@@ -691,8 +735,7 @@ struct efi_gop_pixel {
#define EFI_BLT_BUFFER_TO_VIDEO 2
#define EFI_BLT_VIDEO_TO_VIDEO 3
-struct efi_gop
-{
+struct efi_gop {
efi_status_t (EFIAPI *query_mode)(struct efi_gop *this, u32 mode_number,
efi_uintn_t *size_of_info,
struct efi_gop_mode_info **info);
@@ -762,8 +805,7 @@ struct efi_simple_network_mode {
/* revision of the simple network protocol */
#define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000
-struct efi_simple_network
-{
+struct efi_simple_network {
u64 revision;
efi_status_t (EFIAPI *start)(struct efi_simple_network *this);
efi_status_t (EFIAPI *stop)(struct efi_simple_network *this);
@@ -808,8 +850,7 @@ struct efi_pxe_packet {
u8 packet[1472];
};
-struct efi_pxe_mode
-{
+struct efi_pxe_mode {
u8 started;
u8 ipv6_available;
u8 ipv6_supported;
@@ -958,4 +999,24 @@ struct efi_driver_binding_protocol {
efi_handle_t driver_binding_handle;
};
+#define EFI_UNICODE_COLLATION_PROTOCOL2_GUID \
+ EFI_GUID(0xa4c751fc, 0x23ae, 0x4c3e, \
+ 0x92, 0xe9, 0x49, 0x64, 0xcf, 0x63, 0xf3, 0x49)
+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
diff --git a/include/efi_loader.h b/include/efi_loader.h
index f162adf..34e44c6 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -13,13 +13,18 @@
#include <efi_api.h>
/* No need for efi loader support in SPL */
-#if defined(CONFIG_EFI_LOADER) && !defined(CONFIG_SPL_BUILD)
+#if CONFIG_IS_ENABLED(EFI_LOADER)
#include <linux/list.h>
/* Maximum number of configuration tables */
#define EFI_MAX_CONFIGURATION_TABLES 16
+/* GUID used by the root node */
+#define U_BOOT_GUID \
+ EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \
+ 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b)
+
int __efi_entry_check(void);
int __efi_exit_check(void);
const char *__efi_nesting(void);
@@ -92,15 +97,20 @@ extern struct efi_runtime_services efi_runtime_services;
extern struct efi_system_table systab;
extern struct efi_simple_text_output_protocol efi_con_out;
-extern struct efi_simple_input_interface efi_con_in;
+extern struct efi_simple_text_input_protocol efi_con_in;
extern struct efi_console_control_protocol efi_console_control;
extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
/* implementation of the EFI_DEVICE_PATH_UTILITIES_PROTOCOL */
extern const struct efi_device_path_utilities_protocol
efi_device_path_utilities;
+/* Implementation of the EFI_UNICODE_COLLATION_PROTOCOL */
+extern const struct efi_unicode_collation_protocol
+ efi_unicode_collation_protocol;
uint16_t *efi_dp_str(struct efi_device_path *dp);
+/* GUID of the U-Boot root node */
+extern const efi_guid_t efi_u_boot_guid;
/* GUID of the EFI_BLOCK_IO_PROTOCOL */
extern const efi_guid_t efi_block_io_guid;
extern const efi_guid_t efi_global_variable_guid;
@@ -127,6 +137,8 @@ extern const efi_guid_t efi_file_info_guid;
/* GUID for file system information */
extern const efi_guid_t efi_file_system_info_guid;
extern const efi_guid_t efi_guid_device_path_utilities_protocol;
+/* GUID of the Unicode collation protocol */
+extern const efi_guid_t efi_guid_unicode_collation_protocol;
extern unsigned int __efi_runtime_start, __efi_runtime_stop;
extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -172,6 +184,20 @@ struct efi_object {
};
/**
+ * struct efi_loaded_image_obj - handle of a loaded image
+ */
+struct efi_loaded_image_obj {
+ /* Generic EFI object parent class data */
+ struct efi_object parent;
+ void *reloc_base;
+ aligned_u64 reloc_size;
+ efi_status_t exit_status;
+ struct jmp_buf_data exit_jmp;
+ EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
+ struct efi_system_table *st);
+};
+
+/**
* struct efi_event
*
* @link: Link to list of all events
@@ -205,6 +231,8 @@ extern struct list_head efi_obj_list;
/* List of all events */
extern struct list_head efi_events;
+/* Called by bootefi to initialize root node */
+efi_status_t efi_root_node_register(void);
/* Called by bootefi to initialize runtime */
efi_status_t efi_initialize_system_table(void);
/* Called by bootefi to make console interface available */
@@ -250,7 +278,8 @@ efi_status_t efi_set_watchdog(unsigned long timeout);
/* Called from places to check whether a timer expired */
void efi_timer_check(void);
/* PE loader implementation */
-void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
+void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+ struct efi_loaded_image *loaded_image_info);
/* Called once to store the pristine gd pointer */
void efi_save_gd(void);
/* Special case handler for error/abort that just tries to dtrt to get
@@ -331,14 +360,12 @@ int efi_memory_init(void);
/* Adds new or overrides configuration table entry to the system table */
efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
/* Sets up a loaded image */
-efi_status_t efi_setup_loaded_image(
- struct efi_loaded_image *info, struct efi_object *obj,
- struct efi_device_path *device_path,
- struct efi_device_path *file_path);
+efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
+ struct efi_device_path *file_path,
+ struct efi_loaded_image_obj **handle_ptr,
+ struct efi_loaded_image **info_ptr);
efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
void **buffer);
-/* Print information about a loaded image */
-efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc);
/* Print information about all loaded images */
void efi_print_image_infos(void *pc);
@@ -397,7 +424,15 @@ efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
(((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
-/* Convert strings from normal C strings to uEFI strings */
+/**
+ * ascii2unicode() - convert ASCII string to UTF-16 string
+ *
+ * A zero terminated ASCII string is converted to a zero terminated UTF-16
+ * string. The output buffer must be preassigned.
+ *
+ * @unicode: preassigned output buffer for UTF-16 string
+ * @ascii: ASCII string to be converted
+ */
static inline void ascii2unicode(u16 *unicode, const char *ascii)
{
while (*ascii)
@@ -460,7 +495,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor,
void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path);
-#else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
+#else /* CONFIG_IS_ENABLED(EFI_LOADER) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
#define __efi_runtime_data
@@ -477,6 +512,6 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
static inline void efi_print_image_infos(void *pc) { }
-#endif /* CONFIG_EFI_LOADER && !CONFIG_SPL_BUILD */
+#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
#endif /* _EFI_LOADER_H */
diff --git a/include/efi_selftest.h b/include/efi_selftest.h
index d0a76d7..56beac3 100644
--- a/include/efi_selftest.h
+++ b/include/efi_selftest.h
@@ -53,7 +53,7 @@ enum efi_test_phase {
};
extern struct efi_simple_text_output_protocol *con_out;
-extern struct efi_simple_input_interface *con_in;
+extern struct efi_simple_text_input_protocol *con_in;
/*
* Exit the boot services.
@@ -76,6 +76,22 @@ void efi_st_exit_boot_services(void);
void efi_st_printc(int color, const char *fmt, ...)
__attribute__ ((format (__printf__, 2, 3)));
+/**
+ * efi_st_translate_char() - translate a unicode character to a string
+ *
+ * @code: unicode character
+ * Return: string
+ */
+u16 *efi_st_translate_char(u16 code);
+
+/**
+ * efi_st_translate_code() - translate a scan code to a human readable string
+ *
+ * @code: unicode character
+ * Return: string
+ */
+u16 *efi_st_translate_code(u16 code);
+
/*
* Compare memory.
* We cannot use lib/string.c due to different CFLAGS values.
diff --git a/include/fat.h b/include/fat.h
index 09e1423..bc139f8 100644
--- a/include/fat.h
+++ b/include/fat.h
@@ -173,6 +173,8 @@ typedef struct {
int fatbufnum; /* Used by get_fatent, init to -1 */
int rootdir_size; /* Size of root dir for non-FAT32 */
__u32 root_cluster; /* First cluster of root dir for FAT32 */
+ u32 total_sect; /* Number of sectors */
+ int fats; /* Number of FATs */
} fsdata;
static inline u32 clust_to_sect(fsdata *fsdata, u32 clust)
@@ -201,5 +203,7 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
int fat_opendir(const char *filename, struct fs_dir_stream **dirsp);
int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
void fat_closedir(struct fs_dir_stream *dirs);
+int fat_unlink(const char *filename);
+int fat_mkdir(const char *dirname);
void fat_close(void);
#endif /* _FAT_H_ */
diff --git a/include/fs.h b/include/fs.h
index 163da10..aa3604d 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -156,6 +156,24 @@ struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs);
void fs_closedir(struct fs_dir_stream *dirs);
/*
+ * fs_unlink - delete a file or directory
+ *
+ * If a given name is a directory, it will be deleted only if it's empty
+ *
+ * @filename: Name of file or directory to delete
+ * @return 0 on success, -1 on error conditions
+ */
+int fs_unlink(const char *filename);
+
+/*
+ * fs_mkdir - Create a directory
+ *
+ * @filename: Name of directory to create
+ * @return 0 on success, -1 on error conditions
+ */
+int fs_mkdir(const char *filename);
+
+/*
* Common implementation for various filesystem commands, optionally limited
* to a specific filesystem type via the fstype parameter.
*/
@@ -169,6 +187,10 @@ int file_exists(const char *dev_type, const char *dev_part, const char *file,
int fstype);
int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
int fstype);
+int do_rm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
+ int fstype);
+int do_mkdir(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
+ int fstype);
/*
* Determine the UUID of the specified filesystem and print it. Optionally it is
diff --git a/include/os.h b/include/os.h
index c8e0f52..5c79721 100644
--- a/include/os.h
+++ b/include/os.h
@@ -331,24 +331,7 @@ int os_spl_to_uboot(const char *fname);
void os_localtime(struct rtc_time *rt);
/**
- * os_setjmp() - Call setjmp()
- *
- * Call the host system's setjmp() function.
- *
- * @jmp: Buffer to store current execution state
- * @size: Size of buffer
- * @return normal setjmp() value if OK, -ENOSPC if @size is too small
- */
-int os_setjmp(ulong *jmp, int size);
-
-/**
- * os_longjmp() - Call longjmp()
- *
- * Call the host system's longjmp() function.
- *
- * @jmp: Buffer where previous execution state was stored
- * @ret: Value to pass to longjmp()
+ * os_abort() - Raise SIGABRT to exit sandbox (e.g. to debugger)
*/
-void os_longjmp(ulong *jmp, int ret);
-
+void os_abort(void);
#endif
diff --git a/include/test/suites.h b/include/test/suites.h
index 071ab40..abb3a4b 100644
--- a/include/test/suites.h
+++ b/include/test/suites.h
@@ -23,10 +23,11 @@ struct unit_test;
int cmd_ut_category(const char *name, struct unit_test *tests, int n_ents,
int argc, char * const argv[]);
+int do_ut_compression(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
int do_ut_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
int do_ut_env(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
int do_ut_overlay(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
int do_ut_time(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
-int do_ut_compression(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
+int do_ut_unicode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
#endif /* __TEST_SUITES_H__ */
diff --git a/lib/Makefile b/lib/Makefile
index 5f583ae..f169644 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -19,7 +19,12 @@ obj-$(CONFIG_ARCH_AT91) += at91/
obj-$(CONFIG_OPTEE) += optee/
obj-$(CONFIG_AES) += aes.o
+
+ifndef API_BUILD
+ifneq ($(CONFIG_UT_UNICODE)$(CONFIG_EFI_LOADER),)
obj-y += charset.o
+endif
+endif
obj-$(CONFIG_USB_TTY) += circbuf.o
obj-y += crc7.o
obj-y += crc8.o
diff --git a/lib/charset.c b/lib/charset.c
index cd186a5..0cede9b 100644
--- a/lib/charset.c
+++ b/lib/charset.c
@@ -5,44 +5,343 @@
* Copyright (c) 2017 Rob Clark
*/
+#include <common.h>
#include <charset.h>
+#include <capitalization.h>
#include <malloc.h>
-/*
- * utf8/utf16 conversion mostly lifted from grub
+static struct capitalization_table capitalization_table[] =
+#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION
+ UNICODE_CAPITALIZATION_TABLE;
+#elif CONFIG_FAT_DEFAULT_CODEPAGE == 1250
+ CP1250_CAPITALIZATION_TABLE;
+#else
+ CP437_CAPITALIZATION_TABLE;
+#endif
+
+/**
+ * get_code() - read Unicode code point from UTF-8 stream
+ *
+ * @read_u8: - stream reader
+ * @src: - string buffer passed to stream reader, optional
+ * Return: - Unicode code point
+ */
+static int get_code(u8 (*read_u8)(void *data), void *data)
+{
+ s32 ch = 0;
+
+ ch = read_u8(data);
+ if (!ch)
+ return 0;
+ if (ch >= 0xc2 && ch <= 0xf4) {
+ int code = 0;
+
+ if (ch >= 0xe0) {
+ if (ch >= 0xf0) {
+ /* 0xf0 - 0xf4 */
+ ch &= 0x07;
+ code = ch << 18;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ ch &= 0x3f;
+ } else {
+ /* 0xe0 - 0xef */
+ ch &= 0x0f;
+ }
+ code += ch << 12;
+ if ((code >= 0xD800 && code <= 0xDFFF) ||
+ code >= 0x110000)
+ goto error;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ }
+ /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */
+ ch &= 0x3f;
+ code += ch << 6;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ ch &= 0x3f;
+ ch += code;
+ } else if (ch >= 0x80) {
+ goto error;
+ }
+ return ch;
+error:
+ return '?';
+}
+
+/**
+ * read_string() - read byte from character string
+ *
+ * @data: - pointer to string
+ * Return: - byte read
+ *
+ * The string pointer is incremented if it does not point to '\0'.
*/
+static u8 read_string(void *data)
-size_t utf16_strlen(const uint16_t *in)
{
- size_t i;
- for (i = 0; in[i]; i++);
- return i;
+ const char **src = (const char **)data;
+ u8 c;
+
+ if (!src || !*src || !**src)
+ return 0;
+ c = **src;
+ ++*src;
+ return c;
}
-size_t utf16_strnlen(const uint16_t *in, size_t count)
+/**
+ * read_console() - read byte from console
+ *
+ * @src - not used, needed to match interface
+ * Return: - byte read
+ */
+static u8 read_console(void *data)
{
- size_t i;
- for (i = 0; count-- && in[i]; i++);
- return i;
+ return getc();
+}
+
+int console_read_unicode(s32 *code)
+{
+ if (!tstc()) {
+ /* No input available */
+ return 1;
+ }
+
+ /* Read Unicode code */
+ *code = get_code(read_console, NULL);
+ return 0;
+}
+
+s32 utf8_get(const char **src)
+{
+ return get_code(read_string, src);
+}
+
+int utf8_put(s32 code, char **dst)
+{
+ if (!dst || !*dst)
+ return -1;
+ if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
+ return -1;
+ if (code <= 0x007F) {
+ **dst = code;
+ } else {
+ if (code <= 0x07FF) {
+ **dst = code >> 6 | 0xC0;
+ } else {
+ if (code < 0x10000) {
+ **dst = code >> 12 | 0xE0;
+ } else {
+ **dst = code >> 18 | 0xF0;
+ ++*dst;
+ **dst = (code >> 12 & 0x3F) | 0x80;
+ }
+ ++*dst;
+ **dst = (code >> 6 & 0x3F) | 0x80;
+ }
+ ++*dst;
+ **dst = (code & 0x3F) | 0x80;
+ }
+ ++*dst;
+ return 0;
+}
+
+size_t utf8_utf16_strnlen(const char *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf8_get(&src);
+
+ if (!code)
+ break;
+ if (code < 0) {
+ /* Reserve space for a replacement character */
+ len += 1;
+ } else if (code < 0x10000) {
+ len += 1;
+ } else {
+ len += 2;
+ }
+ }
+ return len;
+}
+
+int utf8_utf16_strncpy(u16 **dst, const char *src, size_t count)
+{
+ if (!src || !dst || !*dst)
+ return -1;
+
+ for (; count && *src; --count) {
+ s32 code = utf8_get(&src);
+
+ if (code < 0)
+ code = '?';
+ utf16_put(code, dst);
+ }
+ **dst = 0;
+ return 0;
+}
+
+s32 utf16_get(const u16 **src)
+{
+ s32 code, code2;
+
+ if (!src || !*src)
+ return -1;
+ if (!**src)
+ return 0;
+ code = **src;
+ ++*src;
+ if (code >= 0xDC00 && code <= 0xDFFF)
+ return -1;
+ if (code >= 0xD800 && code <= 0xDBFF) {
+ if (!**src)
+ return -1;
+ code &= 0x3ff;
+ code <<= 10;
+ code += 0x10000;
+ code2 = **src;
+ ++*src;
+ if (code2 <= 0xDC00 || code2 >= 0xDFFF)
+ return -1;
+ code2 &= 0x3ff;
+ code += code2;
+ }
+ return code;
+}
+
+int utf16_put(s32 code, u16 **dst)
+{
+ if (!dst || !*dst)
+ return -1;
+ if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
+ return -1;
+ if (code < 0x10000) {
+ **dst = code;
+ } else {
+ code -= 0x10000;
+ **dst = code >> 10 | 0xD800;
+ ++*dst;
+ **dst = (code & 0x3ff) | 0xDC00;
+ }
+ ++*dst;
+ return 0;
+}
+
+size_t utf16_strnlen(const u16 *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf16_get(&src);
+
+ if (!code)
+ break;
+ /*
+ * In case of an illegal sequence still reserve space for a
+ * replacement character.
+ */
+ ++len;
+ }
+ return len;
+}
+
+size_t utf16_utf8_strnlen(const u16 *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf16_get(&src);
+
+ if (!code)
+ break;
+ if (code < 0)
+ /* Reserve space for a replacement character */
+ len += 1;
+ else if (code < 0x80)
+ len += 1;
+ else if (code < 0x800)
+ len += 2;
+ else if (code < 0x10000)
+ len += 3;
+ else
+ len += 4;
+ }
+ return len;
}
-uint16_t *utf16_strcpy(uint16_t *dest, const uint16_t *src)
+int utf16_utf8_strncpy(char **dst, const u16 *src, size_t count)
{
- uint16_t *tmp = dest;
+ if (!src || !dst || !*dst)
+ return -1;
- while ((*dest++ = *src++) != '\0')
- /* nothing */;
- return tmp;
+ for (; count && *src; --count) {
+ s32 code = utf16_get(&src);
+ if (code < 0)
+ code = '?';
+ utf8_put(code, dst);
+ }
+ **dst = 0;
+ return 0;
}
-uint16_t *utf16_strdup(const uint16_t *s)
+s32 utf_to_lower(const s32 code)
{
- uint16_t *new;
- if (!s || !(new = malloc((utf16_strlen(s) + 1) * 2)))
- return NULL;
- utf16_strcpy(new, s);
- return new;
+ struct capitalization_table *pos = capitalization_table;
+ s32 ret = code;
+
+ if (code <= 0x7f) {
+ if (code >= 'A' && code <= 'Z')
+ ret += 0x20;
+ return ret;
+ }
+ for (; pos->upper; ++pos) {
+ if (pos->upper == code) {
+ ret = pos->lower;
+ break;
+ }
+ }
+ return ret;
+}
+
+s32 utf_to_upper(const s32 code)
+{
+ struct capitalization_table *pos = capitalization_table;
+ s32 ret = code;
+
+ if (code <= 0x7f) {
+ if (code >= 'a' && code <= 'z')
+ ret -= 0x20;
+ return ret;
+ }
+ for (; pos->lower; ++pos) {
+ if (pos->lower == code) {
+ ret = pos->upper;
+ break;
+ }
+ }
+ return ret;
+}
+
+size_t u16_strlen(const u16 *in)
+{
+ size_t i;
+ for (i = 0; in[i]; i++);
+ return i;
+}
+
+size_t u16_strnlen(const u16 *in, size_t count)
+{
+ size_t i;
+ for (i = 0; count-- && in[i]; i++);
+ return i;
}
/* Convert UTF-16 to UTF-8. */
@@ -97,59 +396,3 @@ uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
return dest;
}
-
-uint16_t *utf8_to_utf16(uint16_t *dest, const uint8_t *src, size_t size)
-{
- while (size--) {
- int extension_bytes;
- uint32_t code;
-
- extension_bytes = 0;
- if (*src <= 0x7f) {
- code = *src++;
- /* Exit on zero byte */
- if (!code)
- size = 0;
- } else if (*src <= 0xbf) {
- /* Illegal code */
- code = '?';
- } else if (*src <= 0xdf) {
- code = *src++ & 0x1f;
- extension_bytes = 1;
- } else if (*src <= 0xef) {
- code = *src++ & 0x0f;
- extension_bytes = 2;
- } else if (*src <= 0xf7) {
- code = *src++ & 0x07;
- extension_bytes = 3;
- } else {
- /* Illegal code */
- code = '?';
- }
-
- for (; extension_bytes && size; --size, --extension_bytes) {
- if ((*src & 0xc0) == 0x80) {
- code <<= 6;
- code |= *src++ & 0x3f;
- } else {
- /* Illegal code */
- code = '?';
- ++src;
- --size;
- break;
- }
- }
-
- if (code < 0x10000) {
- *dest++ = code;
- } else {
- /*
- * Simplified expression for
- * (((code - 0x10000) >> 10) & 0x3ff) | 0xd800
- */
- *dest++ = (code >> 10) + 0xd7c0;
- *dest++ = (code & 0x3ff) | 0xdc00;
- }
- }
- return dest;
-}
diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c
index b484aba..bb86ffd 100644
--- a/lib/efi_driver/efi_uclass.c
+++ b/lib/efi_driver/efi_uclass.c
@@ -19,11 +19,13 @@
#include <efi_driver.h>
-/*
- * Check node type. We do not support partitions as controller handles.
+/**
+ * check_node_type() - check node type
+ *
+ * We do not support partitions as controller handles.
*
- * @handle handle to be checked
- * @return status code
+ * @handle: handle to be checked
+ * Return: status code
*/
static efi_status_t check_node_type(efi_handle_t handle)
{
@@ -44,13 +46,13 @@ static efi_status_t check_node_type(efi_handle_t handle)
return ret;
}
-/*
- * Check if the driver supports the controller.
+/**
+ * efi_uc_supported() - check if the driver supports the controller
*
- * @this driver binding protocol
- * @controller_handle handle of the controller
- * @remaining_device_path path specifying the child controller
- * @return status code
+ * @this: driver binding protocol
+ * @controller_handle: handle of the controller
+ * @remaining_device_path: path specifying the child controller
+ * Return: status code
*/
static efi_status_t EFIAPI efi_uc_supported(
struct efi_driver_binding_protocol *this,
@@ -92,13 +94,13 @@ out:
return EFI_EXIT(ret);
}
-/*
- * Create child controllers and attach driver.
+/**
+ * efi_uc_start() - create child controllers and attach driver
*
- * @this driver binding protocol
- * @controller_handle handle of the controller
- * @remaining_device_path path specifying the child controller
- * @return status code
+ * @this: driver binding protocol
+ * @controller_handle: handle of the controller
+ * @remaining_device_path: path specifying the child controller
+ * Return: status code
*/
static efi_status_t EFIAPI efi_uc_start(
struct efi_driver_binding_protocol *this,
@@ -146,12 +148,13 @@ out:
return EFI_EXIT(ret);
}
-/*
- * Remove a single child controller from the parent controller.
+/**
+ * disconnect_child() - remove a single child controller from the parent
+ * controller
*
- * @controller_handle parent controller
- * @child_handle child controller
- * @return status code
+ * @controller_handle: parent controller
+ * @child_handle: child controller
+ * Return: status code
*/
static efi_status_t disconnect_child(efi_handle_t controller_handle,
efi_handle_t child_handle)
@@ -176,14 +179,14 @@ static efi_status_t disconnect_child(efi_handle_t controller_handle,
return ret;
}
-/*
- * Remove child controllers and disconnect the controller.
+/**
+ * efi_uc_stop() - Remove child controllers and disconnect the controller
*
- * @this driver binding protocol
- * @controller_handle handle of the controller
- * @number_of_children number of child controllers to remove
- * @child_handle_buffer handles of the child controllers to remove
- * @return status code
+ * @this: driver binding protocol
+ * @controller_handle: handle of the controller
+ * @number_of_children: number of child controllers to remove
+ * @child_handle_buffer: handles of the child controllers to remove
+ * Return: status code
*/
static efi_status_t EFIAPI efi_uc_stop(
struct efi_driver_binding_protocol *this,
@@ -241,6 +244,12 @@ out:
return EFI_EXIT(ret);
}
+/**
+ * efi_add_driver() - add driver
+ *
+ * @drv: driver to add
+ * Return: status code
+ */
static efi_status_t efi_add_driver(struct driver *drv)
{
efi_status_t ret;
@@ -280,11 +289,12 @@ out:
return ret;
}
-/*
- * Initialize the EFI drivers.
- * Called by board_init_r().
+/**
+ * efi_driver_init() - initialize the EFI drivers
*
- * @return 0 = success, any other value will stop further execution
+ * Called by efi_init_obj_list().
+ *
+ * Return: 0 = success, any other value will stop further execution
*/
efi_status_t efi_driver_init(void)
{
@@ -309,12 +319,24 @@ efi_status_t efi_driver_init(void)
return ret;
}
+/**
+ * efi_uc_init() - initialize the EFI uclass
+ *
+ * @class: the EFI uclass
+ * Return: 0 = success
+ */
static int efi_uc_init(struct uclass *class)
{
printf("EFI: Initializing UCLASS_EFI\n");
return 0;
}
+/**
+ * efi_uc_destroy() - destroy the EFI uclass
+ *
+ * @class: the EFI uclass
+ * Return: 0 = success
+ */
static int efi_uc_destroy(struct uclass *class)
{
printf("Destroying UCLASS_EFI\n");
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index ce6a09f..b921ea8 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -1,6 +1,6 @@
config EFI_LOADER
bool "Support running EFI Applications in U-Boot"
- depends on (ARM || X86 || RISCV) && OF_LIBFDT
+ depends on (ARM || X86 || RISCV || SANDBOX) && OF_LIBFDT
# We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB
depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT
# We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB
@@ -15,6 +15,16 @@ config EFI_LOADER
interfaces to a loaded EFI application, enabling it to reuse U-Boot's
device drivers.
+config EFI_UNICODE_CAPITALIZATION
+ bool "Support Unicode capitalization"
+ depends on EFI_LOADER
+ default y
+ 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.
+
config EFI_LOADER_BOUNCE_BUFFER
bool "EFI Applications use bounce buffers for DMA operations"
depends on EFI_LOADER && ARM64
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 1ffbf52..6703435 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -17,9 +17,19 @@ always += helloworld.efi
endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
-obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
-obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
-obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o
+obj-y += efi_bootmgr.o
+obj-y += efi_boottime.o
+obj-y += efi_console.o
+obj-y += efi_device_path.o
+obj-y += efi_device_path_to_text.o
+obj-y += efi_device_path_utilities.o
+obj-y += efi_file.o
+obj-y += efi_image_loader.o
+obj-y += efi_memory.o
+obj-y += efi_root_node.o
+obj-y += efi_runtime.o
+obj-y += efi_unicode_collation.o
+obj-y += efi_variable.o
obj-y += efi_watchdog.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 853358a..0c5764d 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -60,7 +60,7 @@ static void parse_load_option(struct load_option *lo, void *ptr)
ptr += sizeof(u16);
lo->label = ptr;
- ptr += (utf16_strlen(lo->label) + 1) * 2;
+ ptr += (u16_strlen(lo->label) + 1) * 2;
lo->file_path = ptr;
ptr += lo->file_path_length;
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index ca61e1a..97eb19c 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -26,14 +26,6 @@ LIST_HEAD(efi_obj_list);
/* List of all events */
LIST_HEAD(efi_events);
-/*
- * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
- * we need to do trickery with caches. Since we don't want to break the EFI
- * aware boot path, only apply hacks when loading exiting directly (breaking
- * direct Linux EFI booting along the way - oh well).
- */
-static bool efi_is_direct_boot = true;
-
#ifdef CONFIG_ARM
/*
* The "gd" pointer lives in a register on ARM and AArch64 that we declare
@@ -105,8 +97,8 @@ void efi_save_gd(void)
/*
* Special case handler for error/abort that just forces things back to u-boot
- * world so we can dump out an abort msg, without any care about returning back
- * to UEFI world.
+ * world so we can dump out an abort message, without any care about returning
+ * back to UEFI world.
*/
void efi_restore_gd(void)
{
@@ -183,7 +175,7 @@ static void efi_queue_event(struct efi_event *event, bool check_tpl)
* is_valid_tpl() - check if the task priority level is valid
*
* @tpl: TPL level to check
- * ReturnValue: status code
+ * Return: status code
*/
efi_status_t is_valid_tpl(efi_uintn_t tpl)
{
@@ -626,7 +618,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
evt->notify_function = notify_function;
evt->notify_context = notify_context;
evt->group = group;
- /* Disable timers on bootup */
+ /* Disable timers on boot up */
evt->trigger_next = -1ULL;
evt->is_queued = false;
evt->is_signaled = false;
@@ -732,7 +724,7 @@ void efi_timer_check(void)
* efi_set_timer() - set the trigger time for a timer event or stop the event
* @event: event for which the timer is set
* @type: type of the timer
- * @trigger_time: trigger period in multiples of 100ns
+ * @trigger_time: trigger period in multiples of 100 ns
*
* This is the function for internal usage in U-Boot. For the API function
* implementing the SetTimer service see efi_set_timer_ext.
@@ -747,8 +739,8 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
return EFI_INVALID_PARAMETER;
/*
- * The parameter defines a multiple of 100ns.
- * We use multiples of 1000ns. So divide by 10.
+ * The parameter defines a multiple of 100 ns.
+ * We use multiples of 1000 ns. So divide by 10.
*/
do_div(trigger_time, 10);
@@ -774,7 +766,7 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
* event
* @event: event for which the timer is set
* @type: type of the timer
- * @trigger_time: trigger period in multiples of 100ns
+ * @trigger_time: trigger period in multiples of 100 ns
*
* This function implements the SetTimer service.
*
@@ -1061,7 +1053,7 @@ out:
/**
* efi_get_drivers() - get all drivers associated to a controller
* @efiobj: handle of the controller
- * @protocol: protocol guid (optional)
+ * @protocol: protocol GUID (optional)
* @number_of_drivers: number of child controllers
* @driver_handle_buffer: handles of the the drivers
*
@@ -1126,7 +1118,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj,
/**
* efi_disconnect_all_drivers() - disconnect all drivers from a controller
* @efiobj: handle of the controller
- * @protocol: protocol guid (optional)
+ * @protocol: protocol GUID (optional)
* @child_handle: handle of the child to destroy
*
* This function implements the DisconnectController service.
@@ -1408,7 +1400,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
if (!guid)
return EFI_INVALID_PARAMETER;
- /* Check for guid override */
+ /* Check for GUID override */
for (i = 0; i < systab.nr_tables; i++) {
if (!guidcmp(guid, &systab.tables[i].guid)) {
if (table)
@@ -1432,7 +1424,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
systab.nr_tables = i + 1;
out:
- /* systab.nr_tables may have changed. So we need to update the crc32 */
+ /* systab.nr_tables may have changed. So we need to update the CRC32 */
efi_update_table_header_crc32(&systab.hdr);
/* Notify that the configuration table was changed */
@@ -1478,20 +1470,35 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
*
* Return: status code
*/
-efi_status_t efi_setup_loaded_image(
- struct efi_loaded_image *info, struct efi_object *obj,
- struct efi_device_path *device_path,
- struct efi_device_path *file_path)
+efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
+ struct efi_device_path *file_path,
+ struct efi_loaded_image_obj **handle_ptr,
+ struct efi_loaded_image **info_ptr)
{
efi_status_t ret;
+ struct efi_loaded_image *info;
+ struct efi_loaded_image_obj *obj;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return EFI_OUT_OF_RESOURCES;
+ obj = calloc(1, sizeof(*obj));
+ if (!obj) {
+ free(info);
+ return EFI_OUT_OF_RESOURCES;
+ }
/* Add internal object to object list */
- efi_add_handle(obj);
- /* efi_exit() assumes that the handle points to the info */
- obj->handle = info;
+ efi_add_handle(&obj->parent);
+
+ if (info_ptr)
+ *info_ptr = info;
+ if (handle_ptr)
+ *handle_ptr = obj;
info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
info->file_path = file_path;
+ info->system_table = &systab;
if (device_path) {
info->device_handle = efi_dp_find_obj(device_path, NULL);
@@ -1499,8 +1506,8 @@ efi_status_t efi_setup_loaded_image(
* When asking for the device path interface, return
* bootefi_device_path
*/
- ret = efi_add_protocol(obj->handle, &efi_guid_device_path,
- device_path);
+ ret = efi_add_protocol(obj->parent.handle,
+ &efi_guid_device_path, device_path);
if (ret != EFI_SUCCESS)
goto failure;
}
@@ -1509,19 +1516,8 @@ efi_status_t efi_setup_loaded_image(
* When asking for the loaded_image interface, just
* return handle which points to loaded_image_info
*/
- ret = efi_add_protocol(obj->handle, &efi_guid_loaded_image, info);
- if (ret != EFI_SUCCESS)
- goto failure;
-
- ret = efi_add_protocol(obj->handle,
- &efi_guid_device_path_to_text_protocol,
- (void *)&efi_device_path_to_text);
- if (ret != EFI_SUCCESS)
- goto failure;
-
- ret = efi_add_protocol(obj->handle,
- &efi_guid_device_path_utilities_protocol,
- (void *)&efi_device_path_utilities);
+ ret = efi_add_protocol(obj->parent.handle,
+ &efi_guid_loaded_image, info);
if (ret != EFI_SUCCESS)
goto failure;
@@ -1604,7 +1600,8 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
efi_handle_t *image_handle)
{
struct efi_loaded_image *info;
- struct efi_object *obj;
+ struct efi_loaded_image_obj **image_obj =
+ (struct efi_loaded_image_obj **)image_handle;
efi_status_t ret;
EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
@@ -1620,18 +1617,6 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
goto error;
}
- info = calloc(1, sizeof(*info));
- if (!info) {
- ret = EFI_OUT_OF_RESOURCES;
- goto error;
- }
- obj = calloc(1, sizeof(*obj));
- if (!obj) {
- free(info);
- ret = EFI_OUT_OF_RESOURCES;
- goto error;
- }
-
if (!source_buffer) {
struct efi_device_path *dp, *fp;
@@ -1643,35 +1628,35 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
* file parts:
*/
efi_dp_split_file_path(file_path, &dp, &fp);
- ret = efi_setup_loaded_image(info, obj, dp, fp);
+ ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
if (ret != EFI_SUCCESS)
goto failure;
} else {
- /* In this case, file_path is the "device" path, ie.
+ /* In this case, file_path is the "device" path, i.e.
* something like a HARDWARE_DEVICE:MEMORY_MAPPED
*/
- ret = efi_setup_loaded_image(info, obj, file_path, NULL);
+ ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info);
if (ret != EFI_SUCCESS)
- goto failure;
+ goto error;
}
- info->reserved = efi_load_pe(source_buffer, info);
- if (!info->reserved) {
+ (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
+ if (!(*image_obj)->entry) {
ret = EFI_UNSUPPORTED;
goto failure;
}
info->system_table = &systab;
info->parent_handle = parent_image;
- *image_handle = obj->handle;
return EFI_EXIT(EFI_SUCCESS);
failure:
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
free(info);
- efi_delete_handle(obj);
error:
return EFI_EXIT(ret);
}
/**
- * efi_start_image() - dall the entry point of an image
+ * efi_start_image() - call the entry point of an image
* @image_handle: handle of the image
* @exit_data_size: size of the buffer
* @exit_data: buffer to receive the exit data of the called image
@@ -1687,18 +1672,14 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
unsigned long *exit_data_size,
s16 **exit_data)
{
- EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
- struct efi_system_table *st);
- struct efi_loaded_image *info = image_handle;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
efi_status_t ret;
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
- entry = info->reserved;
-
- efi_is_direct_boot = false;
/* call the image! */
- if (setjmp(&info->exit_jmp)) {
+ if (setjmp(&image_obj->exit_jmp)) {
/*
* We called the entry point of the child image with EFI_CALL
* in the lines below. The child image called the Exit() boot
@@ -1721,16 +1702,16 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
assert(__efi_entry_check());
debug("%sEFI: %lu returned by started image\n",
__efi_nesting_dec(),
- (unsigned long)((uintptr_t)info->exit_status &
+ (unsigned long)((uintptr_t)image_obj->exit_status &
~EFI_ERROR_MASK));
- return EFI_EXIT(info->exit_status);
+ return EFI_EXIT(image_obj->exit_status);
}
- ret = EFI_CALL(entry(image_handle, &systab));
+ ret = EFI_CALL(image_obj->entry(image_handle, &systab));
/*
* Usually UEFI applications call Exit() instead of returning.
- * But because the world doesn not consist of ponies and unicorns,
+ * But because the world doesn't consist of ponies and unicorns,
* we're happy to emulate that behavior on behalf of a payload
* that forgot.
*/
@@ -1757,17 +1738,11 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
int16_t *exit_data)
{
/*
- * We require that the handle points to the original loaded
- * image protocol interface.
- *
- * For getting the longjmp address this is safer than locating
- * the protocol because the protocol may have been reinstalled
- * pointing to another memory location.
- *
* TODO: We should call the unload procedure of the loaded
* image protocol.
*/
- struct efi_loaded_image *loaded_image_info = (void *)image_handle;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
exit_data_size, exit_data);
@@ -1781,8 +1756,8 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
*/
efi_restore_gd();
- loaded_image_info->exit_status = exit_status;
- longjmp(&loaded_image_info->exit_jmp, 1);
+ image_obj->exit_status = exit_status;
+ longjmp(&image_obj->exit_jmp, 1);
panic("EFI application exited");
}
@@ -1811,21 +1786,6 @@ static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
}
/**
- * efi_exit_caches() - fix up caches for EFI payloads if necessary
- */
-static void efi_exit_caches(void)
-{
-#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
- /*
- * Grub on 32bit ARM needs to have caches disabled before jumping into
- * a zImage, but does not know of all cache layers. Give it a hand.
- */
- if (efi_is_direct_boot)
- cleanup_before_linux();
-#endif
-}
-
-/**
* efi_exit_boot_services() - stop all boot services
* @image_handle: handle of the loaded image
* @map_key: key of the memory map
@@ -1874,17 +1834,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
}
}
- /* TODO Should persist EFI variables here */
+ /* TODO: Should persist EFI variables here */
board_quiesce_devices();
- /* Fix up caches for EFI payloads if necessary */
- efi_exit_caches();
-
/* This stops all lingering devices */
bootm_disable_interrupts();
- /* Disable boottime services */
+ /* Disable boot time services */
systab.con_in_handle = NULL;
systab.con_in = NULL;
systab.con_out_handle = NULL;
@@ -2118,7 +2075,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
++*protocol_buffer_count;
}
- /* Copy guids */
+ /* Copy GUIDs */
if (*protocol_buffer_count) {
size_t j = 0;
@@ -2709,7 +2666,7 @@ static efi_status_t efi_bind_controller(
* efi_connect_single_controller() - connect a single driver to a controller
* @controller_handle: controller
* @driver_image_handle: driver
- * @remain_device_path: remainting path
+ * @remain_device_path: remaining path
*
* Return: status code
*/
@@ -2790,7 +2747,7 @@ static efi_status_t efi_connect_single_controller(
* details.
*
* First all driver binding protocol handles are tried for binding drivers.
- * Afterwards all handles that have openened a protocol of the controller
+ * Afterwards all handles that have opened a protocol of the controller
* with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
*
* Return: status code
@@ -3123,7 +3080,7 @@ struct efi_system_table __efi_runtime_data systab = {
/**
* efi_initialize_system_table() - Initialize system table
*
- * Return Value: status code
+ * Return: status code
*/
efi_status_t efi_initialize_system_table(void)
{
@@ -3135,7 +3092,7 @@ efi_status_t efi_initialize_system_table(void)
sizeof(struct efi_configuration_table),
(void **)&systab.tables);
- /* Set crc32 field in table headers */
+ /* Set CRC32 field in table headers */
efi_update_table_header_crc32(&systab.hdr);
efi_update_table_header_crc32(&efi_runtime_services.hdr);
efi_update_table_header_crc32(&efi_boot_services.hdr);
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index b487288..7ecdbb1 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -42,10 +42,12 @@ static struct cout_mode efi_cout_modes[] = {
},
};
-const efi_guid_t efi_guid_text_output_protocol =
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_input_ex_protocol =
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
const efi_guid_t efi_guid_text_input_protocol =
EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_output_protocol =
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
#define cESC '\x1b'
#define ESC "\x1b"
@@ -111,23 +113,28 @@ static efi_status_t EFIAPI efi_cout_output_string(
{
struct simple_text_output_mode *con = &efi_con_mode;
struct cout_mode *mode = &efi_cout_modes[con->mode];
-
- EFI_ENTRY("%p, %p", this, string);
-
- unsigned int n16 = utf16_strlen(string);
- char buf[MAX_UTF8_PER_UTF16 * n16 + 1];
+ char *buf, *pos;
u16 *p;
+ efi_status_t ret = EFI_SUCCESS;
- *utf16_to_utf8((u8 *)buf, string, n16) = '\0';
+ EFI_ENTRY("%p, %p", this, string);
+ buf = malloc(utf16_utf8_strlen(string) + 1);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = buf;
+ utf16_utf8_strcpy(&pos, string);
fputs(stdout, buf);
+ free(buf);
/*
* Update the cursor position.
*
* The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
* and U000D. All other characters, including control characters
- * U+0007 (bel) and U+0009 (tab), have to increase the column by one.
+ * U+0007 (BEL) and U+0009 (TAB), have to increase the column by one.
*/
for (p = string; *p; ++p) {
switch (*p) {
@@ -158,7 +165,8 @@ static efi_status_t EFIAPI efi_cout_output_string(
con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
}
- return EFI_EXIT(EFI_SUCCESS);
+out:
+ return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_cout_test_string(
@@ -177,32 +185,56 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
return (mode->rows == rows) && (mode->columns == cols);
}
+/**
+ * query_console_serial() - query console size
+ *
+ * @rows pointer to return number of rows
+ * @columns pointer to return number of columns
+ * Returns 0 on success
+ */
static int query_console_serial(int *rows, int *cols)
{
- /* Ask the terminal about its size */
- int n[3];
+ int ret = 0;
+ int n[2];
u64 timeout;
/* Empty input buffer */
while (tstc())
getc();
- printf(ESC"[18t");
+ /*
+ * Not all terminals understand CSI [18t for querying the console size.
+ * We should adhere to escape sequences documented in the console_codes
+ * manpage and the ECMA-48 standard.
+ *
+ * So here we follow a different approach. We position the cursor to the
+ * bottom right and query its position. Before leaving the function we
+ * restore the original cursor position.
+ */
+ printf(ESC "7" /* Save cursor position */
+ ESC "[r" /* Set scrolling region to full window */
+ ESC "[999;999H" /* Move to bottom right corner */
+ ESC "[6n"); /* Query cursor position */
- /* Check if we have a terminal that understands */
+ /* Allow up to one second for a response */
timeout = timer_get_us() + 1000000;
while (!tstc())
- if (timer_get_us() > timeout)
- return -1;
-
- /* Read {depth,rows,cols} */
- if (term_read_reply(n, 3, 't'))
- return -1;
+ if (timer_get_us() > timeout) {
+ ret = -1;
+ goto out;
+ }
- *cols = n[2];
- *rows = n[1];
+ /* Read {rows,cols} */
+ if (term_read_reply(n, 2, 'R')) {
+ ret = 1;
+ goto out;
+ }
- return 0;
+ *cols = n[1];
+ *rows = n[0];
+out:
+ printf(ESC "8"); /* Restore cursor position */
+ return ret;
}
/*
@@ -298,8 +330,8 @@ static const struct {
{ 36, 46 }, /* 3: cyan */
{ 31, 41 }, /* 4: red */
{ 35, 45 }, /* 5: magenta */
- { 33, 43 }, /* 6: brown, map to yellow as edk2 does*/
- { 37, 47 }, /* 7: light grey, map to white */
+ { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/
+ { 37, 47 }, /* 7: light gray, map to white */
};
/* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
@@ -351,13 +383,31 @@ static efi_status_t EFIAPI efi_cout_set_cursor_position(
struct efi_simple_text_output_protocol *this,
unsigned long column, unsigned long row)
{
+ efi_status_t ret = EFI_SUCCESS;
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+
EFI_ENTRY("%p, %ld, %ld", this, column, row);
- printf(ESC"[%d;%df", (int)row, (int)column);
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (row >= mode->rows || column >= mode->columns) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /*
+ * Set cursor position by sending CSI H.
+ * EFI origin is [0, 0], terminal origin is [1, 1].
+ */
+ printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1);
efi_con_mode.cursor_column = column;
efi_con_mode.cursor_row = row;
-
- return EFI_EXIT(EFI_SUCCESS);
+out:
+ return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_cout_enable_cursor(
@@ -384,29 +434,58 @@ struct efi_simple_text_output_protocol efi_con_out = {
.mode = (void*)&efi_con_mode,
};
-static efi_status_t EFIAPI efi_cin_reset(
- struct efi_simple_input_interface *this,
- bool extended_verification)
-{
- EFI_ENTRY("%p, %d", this, extended_verification);
+/**
+ * struct efi_cin_notify_function - registered console input notify function
+ *
+ * @link: link to list
+ * @data: key to notify
+ * @function: function to call
+ */
+struct efi_cin_notify_function {
+ struct list_head link;
+ struct efi_key_data key;
+ efi_status_t (EFIAPI *function)
+ (struct efi_key_data *key_data);
+};
- /* Empty input buffer */
- while (tstc())
- getc();
+static bool key_available;
+static struct efi_key_data next_key;
+static LIST_HEAD(cin_notify_functions);
- return EFI_EXIT(EFI_SUCCESS);
+/**
+ * set_shift_mask() - set shift mask
+ *
+ * @mod: Xterm shift mask
+ */
+void set_shift_mask(int mod, struct efi_key_state *key_state)
+{
+ key_state->key_shift_state = EFI_SHIFT_STATE_VALID;
+ if (mod) {
+ --mod;
+ if (mod & 1)
+ key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED;
+ if (mod & 2)
+ key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED;
+ if (mod & 4)
+ key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED;
+ if (mod & 8)
+ key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
+ } else {
+ key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
+ }
}
-/*
- * Analyze modifiers (shift, alt, ctrl) for function keys.
+/**
+ * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
+ *
* This gets called when we have already parsed CSI.
*
* @modifiers: bitmask (shift, alt, ctrl)
* @return: the unmodified code
*/
-static char skip_modifiers(int *modifiers)
+static int analyze_modifiers(struct efi_key_state *key_state)
{
- char c, mod = 0, ret = 0;
+ int c, mod = 0, ret = 0;
c = getc();
@@ -430,37 +509,38 @@ static char skip_modifiers(int *modifiers)
}
}
out:
- if (mod)
- --mod;
- if (modifiers)
- *modifiers = mod;
+ set_shift_mask(mod, key_state);
if (!ret)
ret = c;
return ret;
}
-static efi_status_t EFIAPI efi_cin_read_key_stroke(
- struct efi_simple_input_interface *this,
- struct efi_input_key *key)
+/**
+ * efi_cin_read_key() - read a key from the console input
+ *
+ * @key: - key received
+ * Return: - status code
+ */
+static efi_status_t efi_cin_read_key(struct efi_key_data *key)
{
struct efi_input_key pressed_key = {
.scan_code = 0,
.unicode_char = 0,
};
- char ch;
+ s32 ch;
- EFI_ENTRY("%p, %p", this, key);
+ if (console_read_unicode(&ch))
+ return EFI_NOT_READY;
- /* We don't do interrupts, so check for timers cooperatively */
- efi_timer_check();
+ key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID;
+ key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID;
- if (!tstc()) {
- /* No key pressed */
- return EFI_EXIT(EFI_NOT_READY);
- }
+ /* We do not support multi-word codes */
+ if (ch >= 0x10000)
+ ch = '?';
- ch = getc();
- if (ch == cESC) {
+ switch (ch) {
+ case 0x1b:
/*
* Xterm Control Sequences
* https://www.xfree86.org/4.8.0/ctlseqs.html
@@ -472,14 +552,13 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
break;
case 'O': /* F1 - F4 */
ch = getc();
- /* skip modifiers */
- if (ch <= '9')
+ /* consider modifiers */
+ if (ch < 'P') {
+ set_shift_mask(ch - '0', &key->key_state);
ch = getc();
+ }
pressed_key.scan_code = ch - 'P' + 11;
break;
- case 'a'...'z':
- ch = ch - 'a';
- break;
case '[':
ch = getc();
switch (ch) {
@@ -493,7 +572,7 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
pressed_key.scan_code = 5;
break;
case '1':
- ch = skip_modifiers(NULL);
+ ch = analyze_modifiers(&key->key_state);
switch (ch) {
case '1'...'5': /* F1 - F5 */
pressed_key.scan_code = ch - '1' + 11;
@@ -513,7 +592,7 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
}
break;
case '2':
- ch = skip_modifiers(NULL);
+ ch = analyze_modifiers(&key->key_state);
switch (ch) {
case '0'...'1': /* F9 - F10 */
pressed_key.scan_code = ch - '0' + 19;
@@ -528,31 +607,406 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
break;
case '3': /* DEL */
pressed_key.scan_code = 8;
- skip_modifiers(NULL);
+ analyze_modifiers(&key->key_state);
break;
case '5': /* PG UP */
pressed_key.scan_code = 9;
- skip_modifiers(NULL);
+ analyze_modifiers(&key->key_state);
break;
case '6': /* PG DOWN */
pressed_key.scan_code = 10;
- skip_modifiers(NULL);
+ analyze_modifiers(&key->key_state);
break;
- }
+ } /* [ */
break;
+ default:
+ /* ALT key */
+ set_shift_mask(3, &key->key_state);
}
- } else if (ch == 0x7f) {
+ break;
+ case 0x7f:
/* Backspace */
ch = 0x08;
}
- if (!pressed_key.scan_code)
+ if (pressed_key.scan_code) {
+ key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID;
+ } else {
pressed_key.unicode_char = ch;
- *key = pressed_key;
- return EFI_EXIT(EFI_SUCCESS);
+ /*
+ * Assume left control key for control characters typically
+ * entered using the control key.
+ */
+ if (ch >= 0x01 && ch <= 0x1f) {
+ key->key_state.key_shift_state |=
+ EFI_SHIFT_STATE_VALID;
+ switch (ch) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1f:
+ key->key_state.key_shift_state |=
+ EFI_LEFT_CONTROL_PRESSED;
+ }
+ }
+ }
+ key->key = pressed_key;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_cin_notify() - notify registered functions
+ */
+static void efi_cin_notify(void)
+{
+ struct efi_cin_notify_function *item;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ bool match = true;
+
+ /* We do not support toggle states */
+ if (item->key.key.unicode_char || item->key.key.scan_code) {
+ if (item->key.key.unicode_char !=
+ next_key.key.unicode_char ||
+ item->key.key.scan_code != next_key.key.scan_code)
+ match = false;
+ }
+ if (item->key.key_state.key_shift_state &&
+ item->key.key_state.key_shift_state !=
+ next_key.key_state.key_shift_state)
+ match = false;
+
+ if (match)
+ /* We don't bother about the return code */
+ EFI_CALL(item->function(&next_key));
+ }
+}
+
+/**
+ * efi_cin_check() - check if keyboard input is available
+ */
+static void efi_cin_check(void)
+{
+ efi_status_t ret;
+
+ if (key_available) {
+ efi_signal_event(efi_con_in.wait_for_key, true);
+ return;
+ }
+
+ if (tstc()) {
+ ret = efi_cin_read_key(&next_key);
+ if (ret == EFI_SUCCESS) {
+ key_available = true;
+
+ /* Notify registered functions */
+ efi_cin_notify();
+
+ /* Queue the wait for key event */
+ if (key_available)
+ efi_signal_event(efi_con_in.wait_for_key, true);
+ }
+ }
+}
+
+/**
+ * efi_cin_empty_buffer() - empty input buffer
+ */
+static void efi_cin_empty_buffer(void)
+{
+ while (tstc())
+ getc();
+ key_available = false;
+}
+
+/**
+ * efi_cin_reset_ex() - reset console input
+ *
+ * @this: - the extended simple text input protocol
+ * @extended_verification: - extended verification
+ *
+ * This function implements the reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static efi_status_t EFIAPI efi_cin_reset_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
}
-struct efi_simple_input_interface efi_con_in = {
+/**
+ * efi_cin_read_key_stroke_ex() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStrokeEx service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key_data);
+
+ /* Check parameters */
+ if (!this || !key_data) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ *key_data = next_key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_set_state() - set toggle key state
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_toggle_state: key toggle state
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_set_state(
+ struct efi_simple_text_input_ex_protocol *this,
+ u8 key_toggle_state)
+{
+ EFI_ENTRY("%p, %u", this, key_toggle_state);
+ /*
+ * U-Boot supports multiple console input sources like serial and
+ * net console for which a key toggle state cannot be set at all.
+ *
+ * According to the UEFI specification it is allowable to not implement
+ * this service.
+ */
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_cin_register_key_notify() - register key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key to be notified
+ * @key_notify_function: function to be called if the key is pressed
+ * @notify_handle: handle for unregistering the notification
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_register_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data,
+ efi_status_t (EFIAPI *key_notify_function)(
+ struct efi_key_data *key_data),
+ void **notify_handle)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_cin_notify_function *notify_function;
+
+ EFI_ENTRY("%p, %p, %p, %p",
+ this, key_data, key_notify_function, notify_handle);
+
+ /* Check parameters */
+ if (!this || !key_data || !key_notify_function || !notify_handle) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n",
+ key_data->key.unicode_char,
+ key_data->key.scan_code,
+ key_data->key_state.key_shift_state,
+ key_data->key_state.key_toggle_state);
+
+ notify_function = calloc(1, sizeof(struct efi_cin_notify_function));
+ if (!notify_function) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ notify_function->key = *key_data;
+ notify_function->function = key_notify_function;
+ list_add_tail(&notify_function->link, &cin_notify_functions);
+ *notify_handle = notify_function;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_unregister_key_notify() - unregister key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @notification_handle: handle received when registering
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_unregister_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ void *notification_handle)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ struct efi_cin_notify_function *item, *notify_function =
+ notification_handle;
+
+ EFI_ENTRY("%p, %p", this, notification_handle);
+
+ /* Check parameters */
+ if (!this || !notification_handle)
+ goto out;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ if (item == notify_function) {
+ ret = EFI_SUCCESS;
+ break;
+ }
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Remove the notify function */
+ list_del(&notify_function->link);
+ free(notify_function);
+out:
+ return EFI_EXIT(ret);
+}
+
+
+/**
+ * efi_cin_reset() - drain the input buffer
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @extended_verification: allow for exhaustive verification
+ * Return: status code
+ *
+ * This function implements the Reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_reset
+ (struct efi_simple_text_input_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStroke service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke
+ (struct efi_simple_text_input_protocol *this,
+ struct efi_input_key *key)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key);
+
+ /* Check parameters */
+ if (!this || !key) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ *key = next_key.key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
+ .reset = efi_cin_reset_ex,
+ .read_key_stroke_ex = efi_cin_read_key_stroke_ex,
+ .wait_for_key_ex = NULL,
+ .set_state = efi_cin_set_state,
+ .register_key_notify = efi_cin_register_key_notify,
+ .unregister_key_notify = efi_cin_unregister_key_notify,
+};
+
+struct efi_simple_text_input_protocol efi_con_in = {
.reset = efi_cin_reset,
.read_key_stroke = efi_cin_read_key_stroke,
.wait_for_key = NULL,
@@ -560,31 +1014,38 @@ struct efi_simple_input_interface efi_con_in = {
static struct efi_event *console_timer_event;
-static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
-{
-}
-
/*
- * Notification function of the console timer event.
+ * efi_console_timer_notify() - notify the console timer event
*
- * event: console timer event
- * context: not used
+ * @event: console timer event
+ * @context: not used
*/
static void EFIAPI efi_console_timer_notify(struct efi_event *event,
void *context)
{
EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
- /* Check if input is available */
- if (tstc()) {
- /* Queue the wait for key event */
- efi_con_in.wait_for_key->is_signaled = true;
- efi_signal_event(efi_con_in.wait_for_key, true);
- }
+/**
+ * efi_key_notify() - notify the wait for key event
+ *
+ * @event: wait for key event
+ * @context: not used
+ */
+static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
EFI_EXIT(EFI_SUCCESS);
}
-/* This gets called from do_bootefi_exec(). */
+/**
+ * efi_console_register() - install the console protocols
+ *
+ * This function is called from do_bootefi_exec().
+ */
int efi_console_register(void)
{
efi_status_t r;
@@ -598,17 +1059,27 @@ int efi_console_register(void)
r = efi_create_handle((efi_handle_t *)&efi_console_output_obj);
if (r != EFI_SUCCESS)
goto out_of_memory;
+
r = efi_add_protocol(efi_console_output_obj->handle,
&efi_guid_text_output_protocol, &efi_con_out);
if (r != EFI_SUCCESS)
goto out_of_memory;
+ systab.con_out_handle = efi_console_output_obj->handle;
+ systab.stderr_handle = efi_console_output_obj->handle;
+
r = efi_create_handle((efi_handle_t *)&efi_console_input_obj);
if (r != EFI_SUCCESS)
goto out_of_memory;
+
r = efi_add_protocol(efi_console_input_obj->handle,
&efi_guid_text_input_protocol, &efi_con_in);
if (r != EFI_SUCCESS)
goto out_of_memory;
+ systab.con_in_handle = efi_console_input_obj->handle;
+ r = efi_add_protocol(efi_console_input_obj->handle,
+ &efi_guid_text_input_ex_protocol, &efi_con_in_ex);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
/* Create console events */
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
@@ -617,6 +1088,7 @@ int efi_console_register(void)
printf("ERROR: Failed to register WaitForKey event\n");
return r;
}
+ efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
efi_console_timer_notify, NULL, NULL,
&console_timer_event);
@@ -630,6 +1102,6 @@ int efi_console_register(void)
printf("ERROR: Failed to set console timer\n");
return r;
out_of_memory:
- printf("ERROR: Out of meemory\n");
+ printf("ERROR: Out of memory\n");
return r;
}
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index 9d776a6..5a61a1c 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -22,10 +22,6 @@ static const struct efi_device_path END = {
.length = sizeof(END),
};
-#define U_BOOT_GUID \
- EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \
- 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b)
-
/* template ROOT node: */
static const struct efi_device_path_vendor ROOT = {
.dp = {
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
index ca8037d..0082236 100644
--- a/lib/efi_loader/efi_device_path_to_text.c
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -17,6 +17,15 @@
const efi_guid_t efi_guid_device_path_to_text_protocol =
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+/**
+ * efi_str_to_u16() - convert ASCII string to UTF-16
+ *
+ * A u16 buffer is allocated from pool. The ASCII string is copied to the u16
+ * buffer.
+ *
+ * @str: ASCII string
+ * Return: UTF-16 string. NULL if out of memory.
+ */
static u16 *efi_str_to_u16(char *str)
{
efi_uintn_t len;
@@ -29,7 +38,6 @@ static u16 *efi_str_to_u16(char *str)
if (ret != EFI_SUCCESS)
return NULL;
ascii2unicode(out, str);
- out[len - 1] = 0;
return out;
}
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index e6a15bc..0753a36 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -9,6 +9,7 @@
#include <charset.h>
#include <efi_loader.h>
#include <malloc.h>
+#include <mapmem.h>
#include <fs.h>
/* GUID for file system information */
@@ -126,11 +127,22 @@ static int sanitize_path(char *path)
return 0;
}
-/* NOTE: despite what you would expect, 'file_name' is actually a path.
- * With windoze style backlashes, ofc.
+/**
+ * file_open() - open a file handle
+ *
+ * @fs: file system
+ * @parent: directory relative to which the file is to be opened
+ * @file_name: path of the file to be opened. '\', '.', or '..' may
+ * be used as modifiers. A leading backslash indicates an
+ * absolute path.
+ * @mode: bit mask indicating the access mode (read, write,
+ * create)
+ * @attributes: attributes for newly created file
+ * Returns: handle to the opened file or NULL
*/
static struct efi_file_handle *file_open(struct file_system *fs,
- struct file_handle *parent, s16 *file_name, u64 mode)
+ struct file_handle *parent, s16 *file_name, u64 mode,
+ u64 attributes)
{
struct file_handle *fh;
char f0[MAX_UTF8_PER_UTF16] = {0};
@@ -139,7 +151,7 @@ static struct efi_file_handle *file_open(struct file_system *fs,
if (file_name) {
utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1);
- flen = utf16_strlen((u16 *)file_name);
+ flen = u16_strlen((u16 *)file_name);
}
/* we could have a parent, but also an absolute path: */
@@ -173,7 +185,12 @@ static struct efi_file_handle *file_open(struct file_system *fs,
if (set_blk_dev(fh))
goto error;
- if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path)))
+ if ((mode & EFI_FILE_MODE_CREATE) &&
+ (attributes & EFI_FILE_DIRECTORY)) {
+ if (fs_mkdir(fh->path))
+ goto error;
+ } else if (!((mode & EFI_FILE_MODE_CREATE) ||
+ fs_exists(fh->path)))
goto error;
/* figure out if file is a directory: */
@@ -195,15 +212,46 @@ static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
s16 *file_name, u64 open_mode, u64 attributes)
{
struct file_handle *fh = to_fh(file);
+ efi_status_t ret;
EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name,
open_mode, attributes);
- *new_handle = file_open(fh->fs, fh, file_name, open_mode);
- if (!*new_handle)
- return EFI_EXIT(EFI_NOT_FOUND);
+ /* Check parameters */
+ if (!file || !new_handle || !file_name) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (open_mode != EFI_FILE_MODE_READ &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
+ EFI_FILE_MODE_CREATE)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /*
+ * The UEFI spec requires that attributes are only set in create mode.
+ * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
+ * read mode. EDK2 does not check that attributes are zero if not in
+ * create mode.
+ *
+ * So here we only check attributes in create mode and do not check
+ * that they are zero otherwise.
+ */
+ if ((open_mode & EFI_FILE_MODE_CREATE) &&
+ (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
- return EFI_EXIT(EFI_SUCCESS);
+ /* Open file */
+ *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
+ if (*new_handle)
+ ret = EFI_SUCCESS;
+ else
+ ret = EFI_NOT_FOUND;
+out:
+ return EFI_EXIT(ret);
}
static efi_status_t file_close(struct file_handle *fh)
@@ -223,9 +271,21 @@ static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
{
struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+
EFI_ENTRY("%p", file);
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ if (fs_unlink(fh->path))
+ ret = EFI_DEVICE_ERROR;
file_close(fh);
- return EFI_EXIT(EFI_WARN_DELETE_FAILURE);
+
+error:
+ return EFI_EXIT(ret);
}
static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
@@ -233,7 +293,7 @@ static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
{
loff_t actread;
- if (fs_read(fh->path, (ulong)buffer, fh->offset,
+ if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
*buffer_size, &actread))
return EFI_DEVICE_ERROR;
@@ -363,7 +423,7 @@ static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
goto error;
}
- if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size,
+ if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
&actwrite)) {
ret = EFI_DEVICE_ERROR;
goto error;
@@ -438,7 +498,7 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
- EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer);
+ EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
if (!guidcmp(info_type, &efi_file_info_guid)) {
struct efi_file_info *info = buffer;
@@ -598,7 +658,7 @@ efi_open_volume(struct efi_simple_file_system_protocol *this,
EFI_ENTRY("%p, %p", this, root);
- *root = file_open(fs, NULL, NULL, 0);
+ *root = file_open(fs, NULL, NULL, 0, 0);
return EFI_EXIT(EFI_SUCCESS);
}
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index fdf40a6..a18ce0a 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -48,20 +48,21 @@ static int machines[] = {
* If the program counter is located within the image the offset to the base
* address is shown.
*
+ * @obj: EFI object
* @image: loaded image
* @pc: program counter (use NULL to suppress offset output)
* @return: status code
*/
-efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc)
+static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
+ struct efi_loaded_image *image,
+ void *pc)
{
- if (!image)
- return EFI_INVALID_PARAMETER;
printf("UEFI image");
printf(" [0x%p:0x%p]",
- image->reloc_base, image->reloc_base + image->reloc_size - 1);
- if (pc && pc >= image->reloc_base &&
- pc < image->reloc_base + image->reloc_size)
- printf(" pc=0x%zx", pc - image->reloc_base);
+ obj->reloc_base, obj->reloc_base + obj->reloc_size - 1);
+ if (pc && pc >= obj->reloc_base &&
+ pc < obj->reloc_base + obj->reloc_size)
+ printf(" pc=0x%zx", pc - obj->reloc_base);
if (image->file_path)
printf(" '%pD'", image->file_path);
printf("\n");
@@ -82,6 +83,7 @@ void efi_print_image_infos(void *pc)
list_for_each_entry(handler, &efiobj->protocols, link) {
if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
efi_print_image_info(
+ (struct efi_loaded_image_obj *)efiobj,
handler->protocol_interface, pc);
}
}
@@ -196,7 +198,8 @@ static void efi_set_code_and_data_type(
* piece of memory. On successful load it then returns the entry point for
* the binary. Otherwise NULL.
*/
-void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
+void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+ struct efi_loaded_image *loaded_image_info)
{
IMAGE_NT_HEADERS32 *nt;
IMAGE_DOS_HEADER *dos;
@@ -314,8 +317,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
/* Populate the loaded image interface bits */
loaded_image_info->image_base = efi;
loaded_image_info->image_size = image_size;
- loaded_image_info->reloc_base = efi_reloc;
- loaded_image_info->reloc_size = virt_size;
+ handle->reloc_base = efi_reloc;
+ handle->reloc_size = virt_size;
return entry;
}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 0ac4ff5..5bd4f4d 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -65,9 +65,54 @@ static int efi_mem_cmp(void *priv, struct list_head *a, struct list_head *b)
return -1;
}
+static uint64_t desc_get_end(struct efi_mem_desc *desc)
+{
+ return desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT);
+}
+
static void efi_mem_sort(void)
{
+ struct list_head *lhandle;
+ struct efi_mem_list *prevmem = NULL;
+ bool merge_again = true;
+
list_sort(NULL, &efi_mem, efi_mem_cmp);
+
+ /* Now merge entries that can be merged */
+ while (merge_again) {
+ merge_again = false;
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+ struct efi_mem_desc *prev = &prevmem->desc;
+ struct efi_mem_desc *cur;
+ uint64_t pages;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ if (!prevmem) {
+ prevmem = lmem;
+ continue;
+ }
+
+ cur = &lmem->desc;
+
+ if ((desc_get_end(cur) == prev->physical_start) &&
+ (prev->type == cur->type) &&
+ (prev->attribute == cur->attribute)) {
+ /* There is an existing map before, reuse it */
+ pages = cur->num_pages;
+ prev->num_pages += pages;
+ prev->physical_start -= pages << EFI_PAGE_SHIFT;
+ prev->virtual_start -= pages << EFI_PAGE_SHIFT;
+ list_del(&lmem->link);
+ free(lmem);
+
+ merge_again = true;
+ break;
+ }
+
+ prevmem = lmem;
+ }
+ }
}
/** efi_mem_carve_out - unmap memory region
@@ -303,7 +348,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
switch (type) {
case EFI_ALLOCATE_ANY_PAGES:
/* Any page */
- addr = efi_find_free_memory(len, gd->start_addr_sp);
+ addr = efi_find_free_memory(len, -1ULL);
if (!addr) {
r = EFI_NOT_FOUND;
break;
diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c
new file mode 100644
index 0000000..b056ba3
--- /dev/null
+++ b/lib/efi_loader/efi_root_node.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Root node for system services
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_u_boot_guid = U_BOOT_GUID;
+
+struct efi_root_dp {
+ struct efi_device_path_vendor vendor;
+ struct efi_device_path end;
+} __packed;
+
+/**
+ * efi_root_node_register() - create root node
+ *
+ * Create the root node on which we install all protocols that are
+ * not related to a loaded image or a driver.
+ *
+ * Return: status code
+ */
+efi_status_t efi_root_node_register(void)
+{
+ efi_handle_t root;
+ efi_status_t ret;
+ struct efi_root_dp *dp;
+
+ /* Create handle */
+ ret = efi_create_handle(&root);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* Install device path protocol */
+ dp = calloc(1, sizeof(*dp));
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Fill vendor node */
+ dp->vendor.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ dp->vendor.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ dp->vendor.dp.length = sizeof(struct efi_device_path_vendor);
+ dp->vendor.guid = efi_u_boot_guid;
+
+ /* Fill end node */
+ dp->end.type = DEVICE_PATH_TYPE_END;
+ dp->end.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ dp->end.length = sizeof(struct efi_device_path);
+
+ /* Install device path protocol */
+ ret = efi_add_protocol(root, &efi_guid_device_path, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /* Install device path to text protocol */
+ ret = efi_add_protocol(root, &efi_guid_device_path_to_text_protocol,
+ (void *)&efi_device_path_to_text);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /* Install device path utilities protocol */
+ ret = efi_add_protocol(root, &efi_guid_device_path_utilities_protocol,
+ (void *)&efi_device_path_utilities);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /* Install Unicode collation protocol */
+ ret = efi_add_protocol(root, &efi_guid_unicode_collation_protocol,
+ (void *)&efi_unicode_collation_protocol);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+failure:
+ return ret;
+}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 27136cb..c5fbd91 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -30,8 +30,9 @@ static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
/*
- * TODO(sjg@chromium.org): These defines and structs should come from the elf
- * header for each arch (or a generic header) rather than being repeated here.
+ * TODO(sjg@chromium.org): These defines and structures should come from the ELF
+ * header for each architecture (or a generic header) rather than being repeated
+ * here.
*/
#if defined(__aarch64__)
#define R_RELATIVE R_AARCH64_RELATIVE
@@ -79,7 +80,7 @@ struct elf_rela {
};
/*
- * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI
+ * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI
* payload are running concurrently at the same time. In this mode, we can
* handle a good number of runtime callbacks
*/
@@ -97,7 +98,7 @@ void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table)
}
/**
- * efi_reset_system_boottime() - reset system at boottime
+ * efi_reset_system_boottime() - reset system at boot time
*
* This function implements the ResetSystem() runtime service before
* SetVirtualAddressMap() is called.
@@ -144,7 +145,7 @@ static void EFIAPI efi_reset_system_boottime(
}
/**
- * efi_get_time_boottime() - get current time at boottime
+ * efi_get_time_boottime() - get current time at boot time
*
* This function implements the GetTime runtime service before
* SetVirtualAddressMap() is called.
@@ -335,7 +336,7 @@ static void efi_runtime_detach(ulong offset)
*p = newaddr;
}
- /* Update crc32 */
+ /* Update CRC32 */
efi_update_table_header_crc32(&efi_runtime_services.hdr);
}
@@ -489,7 +490,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
* available at runtime.
*
* @mmio_ptr: address of the memory-mapped IO region
- * @len: size of thememory-mapped IO region
+ * @len: size of the memory-mapped IO region
* Returns: status code
*/
efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
@@ -607,7 +608,7 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule(
*
* @capsule_header_array: pointer to array of virtual pointers
* @capsule_count: number of pointers in capsule_header_array
- * @capsule_size: maximum capsule size
+ * @maximum_capsule_size: maximum capsule size
* @reset_type: type of reset needed for capsule update
* Returns: status code
*/
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
new file mode 100644
index 0000000..7f3ea3c
--- /dev/null
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Unicode collation protocol
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <cp1250.h>
+#include <cp437.h>
+#include <efi_loader.h>
+
+/* Characters that may not be used in file names */
+static const char illegal[] = "<>:\"/\\|?*";
+
+/*
+ * EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
+ * Linux defaults to codepage 437 for FAT 8.3 file names.
+ */
+#if CONFIG_FAT_DEFAULT_CODEPAGE == 1250
+/* Unicode code points for code page 1250 characters 0x80 - 0xff */
+static const u16 codepage[] = CP1250;
+#else
+/* Unicode code points for code page 437 characters 0x80 - 0xff */
+static const u16 codepage[] = CP437;
+#endif
+
+/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL */
+const efi_guid_t efi_guid_unicode_collation_protocol =
+ EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
+
+/**
+ * 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_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * TODO:
+ * The implementation does not follow the Unicode collation algorithm.
+ * For ASCII characters it results in the same sort order as EDK2.
+ * We could use table UNICODE_CAPITALIZATION_TABLE for better results.
+ *
+ * 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;
+}
+
+/**
+ * metai_match() - compare utf-16 string with a pattern string case-insenitively
+ *
+ * @s: string to compare
+ * @p: 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 *s, const u16 *p)
+{
+ u16 first;
+
+ for (; *s && *p; ++s, ++p) {
+ switch (*p) {
+ case '*':
+ /* Match 0 or more characters */
+ ++p;
+ for (;; ++s) {
+ if (metai_match(s, p))
+ return true;
+ if (!*s)
+ return false;
+ }
+ case '?':
+ /* Match any one character */
+ break;
+ case '[':
+ /* Match any character in the set */
+ ++p;
+ first = *p;
+ if (first == ']')
+ /* Empty set */
+ return false;
+ ++p;
+ if (*p == '-') {
+ /* Range */
+ ++p;
+ if (*s < first || *s > *p)
+ return false;
+ ++p;
+ if (*p != ']')
+ return false;
+ } else {
+ /* Set */
+ bool hit = false;
+
+ if (*s == first)
+ hit = true;
+ for (; *p && *p != ']'; ++p) {
+ if (*p == *s)
+ hit = true;
+ }
+ if (!hit || *p != ']')
+ return false;
+ }
+ break;
+ default:
+ /* Match one character */
+ if (*p != *s)
+ return false;
+ }
+ }
+ if (!*p && !*s)
+ return true;
+ return false;
+}
+
+/**
+ * efi_metai_match() - compare utf-16 string with a pattern string
+ * case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @s: string to compare
+ * @p: 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_PROTOCOL.
+ *
+ * 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
+ * @p: pattern string
+ *
+ * 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_PROTOCOL.
+ */
+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
+ * @p: pattern string
+ *
+ * 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_PROTOCOL.
+ */
+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_PROTOCOL.
+ */
+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[i - 0x80];
+ string[i] = c;
+ if (!c)
+ break;
+ }
+ string[i] = 0;
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - 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_PROTOCOL.
+ *
+ * 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 (c >= 0x80) {
+ int j;
+
+ /* Look for codepage translation */
+ for (j = 0; j < 0x80; ++j) {
+ if (c == codepage[j]) {
+ c = j + 0x80;
+ break;
+ }
+ }
+ if (j >= 0x80) {
+ c = '_';
+ ret = true;
+ }
+ } else if (c && (c < 0x20 || strchr(illegal, c))) {
+ c = '_';
+ ret = true;
+ }
+
+ fat[i] = c;
+ if (!c)
+ break;
+ ++i;
+ }
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = {
+ .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",
+};
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 90b6372..a1313fa 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -44,10 +44,7 @@
* converted to utf16?
*/
-#define MAX_VAR_NAME 31
-#define MAX_NATIVE_VAR_NAME \
- (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \
- (MAX_VAR_NAME * MAX_UTF8_PER_UTF16))
+#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
static int hex(int ch)
{
@@ -101,18 +98,20 @@ static char *mem2hex(char *hexstr, const u8 *mem, int count)
return hexstr;
}
-static efi_status_t efi_to_native(char *native, u16 *variable_name,
+static efi_status_t efi_to_native(char **native, const u16 *variable_name,
efi_guid_t *vendor)
{
size_t len;
+ char *pos;
- len = utf16_strlen((u16 *)variable_name);
- if (len >= MAX_VAR_NAME)
- return EFI_DEVICE_ERROR;
+ len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
+ *native = malloc(len);
+ if (!*native)
+ return EFI_OUT_OF_RESOURCES;
- native += sprintf(native, "efi_%pUl_", vendor);
- native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len);
- *native = '\0';
+ pos = *native;
+ pos += sprintf(pos, "efi_%pUl_", vendor);
+ utf16_utf8_strcpy(&pos, variable_name);
return EFI_SUCCESS;
}
@@ -168,7 +167,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor,
u32 *attributes, efi_uintn_t *data_size,
void *data)
{
- char native_name[MAX_NATIVE_VAR_NAME + 1];
+ char *native_name;
efi_status_t ret;
unsigned long in_size;
const char *val, *s;
@@ -180,13 +179,14 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor,
if (!variable_name || !vendor || !data_size)
return EFI_EXIT(EFI_INVALID_PARAMETER);
- ret = efi_to_native(native_name, variable_name, vendor);
+ ret = efi_to_native(&native_name, variable_name, vendor);
if (ret)
return EFI_EXIT(ret);
debug("%s: get '%s'\n", __func__, native_name);
val = env_get(native_name);
+ free(native_name);
if (!val)
return EFI_EXIT(EFI_NOT_FOUND);
@@ -256,35 +256,41 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor,
u32 attributes, efi_uintn_t data_size,
void *data)
{
- char native_name[MAX_NATIVE_VAR_NAME + 1];
+ char *native_name = NULL, *val = NULL, *s;
efi_status_t ret = EFI_SUCCESS;
- char *val, *s;
u32 attr;
EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
data_size, data);
- if (!variable_name || !vendor)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!variable_name || !vendor) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
- ret = efi_to_native(native_name, variable_name, vendor);
+ ret = efi_to_native(&native_name, variable_name, vendor);
if (ret)
- return EFI_EXIT(ret);
+ goto out;
#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
/* delete the variable: */
env_set(native_name, NULL);
- return EFI_EXIT(EFI_SUCCESS);
+ ret = EFI_SUCCESS;
+ goto out;
}
val = env_get(native_name);
if (val) {
parse_attr(val, &attr);
- if (attr & READ_ONLY)
- return EFI_EXIT(EFI_WRITE_PROTECTED);
+ if (attr & READ_ONLY) {
+ /* We should not free val */
+ val = NULL;
+ ret = EFI_WRITE_PROTECTED;
+ goto out;
+ }
}
val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
@@ -320,6 +326,8 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor,
if (env_set(native_name, val))
ret = EFI_DEVICE_ERROR;
+out:
+ free(native_name);
free(val);
return EFI_EXIT(ret);
diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig
index 59f9f36..b526967 100644
--- a/lib/efi_selftest/Kconfig
+++ b/lib/efi_selftest/Kconfig
@@ -1,6 +1,6 @@
config CMD_BOOTEFI_SELFTEST
bool "Allow booting an EFI efi_selftest"
- depends on CMD_BOOTEFI
+ depends on CMD_BOOTEFI && !SANDBOX
imply FAT
imply FAT_WRITE
help
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index 590f90b..2f55d9d 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -24,12 +24,15 @@ efi_selftest_event_groups.o \
efi_selftest_exitbootservices.o \
efi_selftest_fdt.o \
efi_selftest_gop.o \
+efi_selftest_loaded_image.o \
efi_selftest_manageprotocols.o \
efi_selftest_rtc.o \
efi_selftest_snp.o \
efi_selftest_textinput.o \
+efi_selftest_textinputex.o \
efi_selftest_textoutput.o \
efi_selftest_tpl.o \
+efi_selftest_unicode_collation.o \
efi_selftest_util.o \
efi_selftest_variables.o \
efi_selftest_watchdog.o
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
index eb139c1..42f51b6 100644
--- a/lib/efi_selftest/efi_selftest_console.c
+++ b/lib/efi_selftest/efi_selftest_console.c
@@ -9,7 +9,7 @@
#include <vsprintf.h>
struct efi_simple_text_output_protocol *con_out;
-struct efi_simple_input_interface *con_in;
+struct efi_simple_text_input_protocol *con_in;
/*
* Print a MAC address to an u16 string
diff --git a/lib/efi_selftest/efi_selftest_loaded_image.c b/lib/efi_selftest/efi_selftest_loaded_image.c
new file mode 100644
index 0000000..f9b54ae
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_loaded_image.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_loaded_image
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the Loaded Image Protocol.
+ */
+
+#include <efi_selftest.h>
+
+static efi_guid_t loaded_image_protocol_guid =
+ EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2,
+ 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b);
+static struct efi_boot_services *boottime;
+efi_handle_t image_handle;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+ image_handle = img_handle;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Verify that the loaded image protocol is installed on the image handle.
+ * Verify that the loaded image protocol points to the system table.
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_uintn_t i, protocol_buffer_count = 0;
+ efi_guid_t **protocol_buffer = NULL;
+ bool found = false;
+ struct efi_loaded_image *loaded_image_protocol;
+
+ /*
+ * Get the GUIDs of all protocols installed on the handle.
+ */
+ ret = boottime->protocols_per_handle(image_handle, &protocol_buffer,
+ &protocol_buffer_count);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ProtocolsPerHandle failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!protocol_buffer_count | !protocol_buffer) {
+ efi_st_error("ProtocolsPerHandle returned no protocol\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("%u protocols installed on image handle\n",
+ (unsigned int)protocol_buffer_count);
+ for (i = 0; i < protocol_buffer_count; ++i) {
+ if (efi_st_memcmp(protocol_buffer[i],
+ &loaded_image_protocol_guid,
+ sizeof(efi_guid_t)))
+ found = true;
+ }
+ if (!found) {
+ efi_st_printf("LoadedImageProtocol not found\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(protocol_buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Open the loaded image protocol.
+ */
+ ret = boottime->open_protocol(image_handle, &loaded_image_protocol_guid,
+ (void **)&loaded_image_protocol, NULL,
+ NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("OpenProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (loaded_image_protocol->revision !=
+ EFI_LOADED_IMAGE_PROTOCOL_REVISION) {
+ efi_st_printf("Incorrect revision\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!loaded_image_protocol->system_table ||
+ loaded_image_protocol->system_table->hdr.signature !=
+ EFI_SYSTEM_TABLE_SIGNATURE) {
+ efi_st_printf("System table reference missing\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(loadedimage) = {
+ .name = "loaded image",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_manageprotocols.c b/lib/efi_selftest/efi_selftest_manageprotocols.c
index 44b8da3..b09e4cd 100644
--- a/lib/efi_selftest/efi_selftest_manageprotocols.c
+++ b/lib/efi_selftest/efi_selftest_manageprotocols.c
@@ -179,7 +179,12 @@ static int execute(void)
efi_st_error("LocateHandleBuffer failed to locate new handle\n");
return EFI_ST_FAILURE;
}
- boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+ /* Release buffer */
+ ret = boottime->free_pool(buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
/*
* Test error handling in UninstallMultipleProtocols
@@ -221,6 +226,7 @@ static int execute(void)
efi_st_error("LocateHandleBuffer failed to locate new handle\n");
return EFI_ST_FAILURE;
}
+ /* Clear the buffer, we are reusing it it the next step. */
boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
/*
@@ -248,7 +254,12 @@ static int execute(void)
efi_st_error("LocateHandle failed to locate new handles\n");
return EFI_ST_FAILURE;
}
- boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+ /* Release buffer */
+ ret = boottime->free_pool(buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
/*
* Test LocateProtocol
@@ -319,6 +330,12 @@ static int execute(void)
efi_st_error("Failed to get protocols per handle\n");
return EFI_ST_FAILURE;
}
+ /* Release buffer */
+ ret = boottime->free_pool(prot_buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
/*
* Uninstall remaining protocols
diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c
index 7aa84de..164fbff 100644
--- a/lib/efi_selftest/efi_selftest_textinput.c
+++ b/lib/efi_selftest/efi_selftest_textinput.c
@@ -14,113 +14,8 @@
#include <efi_selftest.h>
-struct translate {
- u16 code;
- u16 *text;
-};
-
static struct efi_boot_services *boottime;
-static struct translate control_characters[] = {
- {0, L"Null"},
- {8, L"BS"},
- {9, L"TAB"},
- {10, L"LF"},
- {13, L"CR"},
- {0, NULL},
-};
-
-static u16 ch[] = L"' '";
-static u16 unknown[] = L"unknown";
-
-static struct translate scan_codes[] = {
- {0x00, L"Null"},
- {0x01, L"Up"},
- {0x02, L"Down"},
- {0x03, L"Right"},
- {0x04, L"Left"},
- {0x05, L"Home"},
- {0x06, L"End"},
- {0x07, L"Insert"},
- {0x08, L"Delete"},
- {0x09, L"Page Up"},
- {0x0a, L"Page Down"},
- {0x0b, L"FN 1"},
- {0x0c, L"FN 2"},
- {0x0d, L"FN 3"},
- {0x0e, L"FN 4"},
- {0x0f, L"FN 5"},
- {0x10, L"FN 6"},
- {0x11, L"FN 7"},
- {0x12, L"FN 8"},
- {0x13, L"FN 9"},
- {0x14, L"FN 10"},
- {0x15, L"FN 11"},
- {0x16, L"FN 12"},
- {0x17, L"Escape"},
- {0x68, L"FN 13"},
- {0x69, L"FN 14"},
- {0x6a, L"FN 15"},
- {0x6b, L"FN 16"},
- {0x6c, L"FN 17"},
- {0x6d, L"FN 18"},
- {0x6e, L"FN 19"},
- {0x6f, L"FN 20"},
- {0x70, L"FN 21"},
- {0x71, L"FN 22"},
- {0x72, L"FN 23"},
- {0x73, L"FN 24"},
- {0x7f, L"Mute"},
- {0x80, L"Volume Up"},
- {0x81, L"Volume Down"},
- {0x100, L"Brightness Up"},
- {0x101, L"Brightness Down"},
- {0x102, L"Suspend"},
- {0x103, L"Hibernate"},
- {0x104, L"Toggle Display"},
- {0x105, L"Recovery"},
- {0x106, L"Reject"},
- {0x0, NULL},
-};
-
-/*
- * Translate a unicode character to a string.
- *
- * @code unicode character
- * @return string
- */
-static u16 *translate_char(u16 code)
-{
- struct translate *tr;
-
- if (code >= ' ') {
- ch[1] = code;
- return ch;
- }
- for (tr = control_characters; tr->text; ++tr) {
- if (tr->code == code)
- return tr->text;
- }
- return unknown;
-}
-
-/*
- * Translate a scan code to a human readable string.
- *
- * @code unicode character
- * @return string
- */
-static u16 *translate_code(u16 code)
-{
- struct translate *tr;
-
- for (tr = scan_codes; tr->text; ++tr) {
- if (tr->code == code)
- return tr->text;
- }
- return unknown;
-}
-
/*
* Setup unit test.
*
@@ -145,24 +40,45 @@ static int execute(void)
{
struct efi_input_key input_key = {0};
efi_status_t ret;
+ efi_uintn_t index;
+
+ /* Drain the console input */
+ ret = con_in->reset(con_in, true);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Reset failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error("Empty buffer not reported\n");
+ return EFI_ST_FAILURE;
+ }
efi_st_printf("Waiting for your input\n");
efi_st_printf("To terminate type 'x'\n");
for (;;) {
/* Wait for next key */
- do {
- ret = con_in->read_key_stroke(con_in, &input_key);
- } while (ret == EFI_NOT_READY);
+ ret = boottime->wait_for_event(1, &con_in->wait_for_key,
+ &index);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("WaitForEvent failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ReadKeyStroke failed\n");
+ return EFI_ST_FAILURE;
+ }
/* Allow 5 minutes until time out */
boottime->set_watchdog_timer(300, 0, 0, NULL);
efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n",
(unsigned int)input_key.unicode_char,
- translate_char(input_key.unicode_char),
+ efi_st_translate_char(input_key.unicode_char),
(unsigned int)input_key.scan_code,
- translate_code(input_key.scan_code));
+ efi_st_translate_code(input_key.scan_code));
switch (input_key.unicode_char) {
case 'x':
diff --git a/lib/efi_selftest/efi_selftest_textinputex.c b/lib/efi_selftest/efi_selftest_textinputex.c
new file mode 100644
index 0000000..de44224
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_textinputex.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_textinput
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ * The unicode character and the scan code are printed for text
+ * input. To run the test:
+ *
+ * setenv efi_selftest extended text input
+ * bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+static const efi_guid_t text_input_ex_protocol_guid =
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+
+static struct efi_simple_text_input_ex_protocol *con_in_ex;
+
+static struct efi_boot_services *boottime;
+
+static void *efi_key_notify_handle;
+static bool efi_running;
+
+/**
+ * efi_key_notify_function() - key notification function
+ *
+ * This function is called when the registered key is hit.
+ *
+ * @key_data: next key
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_key_notify_function
+ (struct efi_key_data *key_data)
+{
+ efi_running = false;
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ struct efi_key_data key_data = {
+ .key = {
+ .scan_code = 0,
+ .unicode_char = 0x18
+ },
+ .key_state = {
+ .key_shift_state = EFI_SHIFT_STATE_VALID |
+ EFI_LEFT_CONTROL_PRESSED,
+ .key_toggle_state = EFI_TOGGLE_STATE_INVALID,
+ },
+ };
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&text_input_ex_protocol_guid, NULL,
+ (void **)&con_in_ex);
+ if (ret != EFI_SUCCESS) {
+ con_in_ex = NULL;
+ efi_st_error
+ ("Extended text input protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = con_in_ex->register_key_notify(con_in_ex, &key_data,
+ efi_key_notify_function,
+ &efi_key_notify_handle);
+ if (ret != EFI_SUCCESS) {
+ efi_key_notify_handle = NULL;
+ efi_st_error
+ ("Notify function could not be registered.\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_running = true;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Unregister notify function.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ ret = con_in_ex->unregister_key_notify
+ (con_in_ex, efi_key_notify_handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error
+ ("Notify function could not be registered.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ struct efi_key_data input_key = { {0, 0}, {0, 0} };
+ efi_status_t ret;
+ efi_uintn_t index;
+
+ if (!con_in_ex) {
+ efi_st_printf("Setup failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Drain the console input */
+ ret = con_in_ex->reset(con_in_ex, true);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Reset failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error("Empty buffer not reported\n");
+ return EFI_ST_FAILURE;
+ }
+
+ efi_st_printf("Waiting for your input\n");
+ efi_st_printf("To terminate type 'CTRL+x'\n");
+
+ while (efi_running) {
+ /* Wait for next key */
+ ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex,
+ &index);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("WaitForEvent failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ReadKeyStroke failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Allow 5 minutes until time out */
+ boottime->set_watchdog_timer(300, 0, 0, NULL);
+
+ efi_st_printf("Unicode char %u (%ps), scan code %u (",
+ (unsigned int)input_key.key.unicode_char,
+ efi_st_translate_char(input_key.key.unicode_char),
+ (unsigned int)input_key.key.scan_code);
+ if (input_key.key_state.key_shift_state &
+ EFI_SHIFT_STATE_VALID) {
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED))
+ efi_st_printf("SHIFT+");
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_ALT_PRESSED | EFI_RIGHT_ALT_PRESSED))
+ efi_st_printf("ALT+");
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_CONTROL_PRESSED |
+ EFI_RIGHT_CONTROL_PRESSED))
+ efi_st_printf("CTRL+");
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_LOGO_PRESSED | EFI_RIGHT_LOGO_PRESSED))
+ efi_st_printf("META+");
+ if (input_key.key_state.key_shift_state ==
+ EFI_SHIFT_STATE_VALID)
+ efi_st_printf("+");
+ }
+
+ efi_st_printf("%ps)\n",
+ efi_st_translate_code(input_key.key.scan_code));
+
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(textinputex) = {
+ .name = "extended text input",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_unicode_collation.c b/lib/efi_selftest/efi_selftest_unicode_collation.c
new file mode 100644
index 0000000..9765bd3
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_unicode_collation.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_unicode_collation
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test unicode collation protocol.
+ */
+
+#include <efi_selftest.h>
+
+static const efi_guid_t unicode_collation_protocol_guid =
+ EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
+
+static struct efi_boot_services *boottime;
+
+static struct efi_unicode_collation_protocol *unicode_collation_protocol;
+
+/**
+ * setup() - setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * ReturnValue: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&unicode_collation_protocol_guid, NULL,
+ (void **)&unicode_collation_protocol);
+ if (ret != EFI_SUCCESS) {
+ unicode_collation_protocol = NULL;
+ efi_st_error("Unicode collation protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_stri_coll(void)
+{
+ efi_intn_t ret;
+ u16 c1[] = L"first";
+ u16 c2[] = L"FIRST";
+ u16 c3[] = L"second";
+
+ ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
+ c1, c2);
+ if (ret) {
+ efi_st_error(
+ "stri_coll(\"%ps\", \"%ps\") = %zu\n", c1, c2, ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
+ c1, c3);
+ if (ret >= 0) {
+ efi_st_error(
+ "stri_coll(\"%ps\", \"%ps\") = %zu\n", c1, c3, ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
+ c3, c1);
+ if (ret <= 0) {
+ efi_st_error(
+ "stri_coll(\"%ps\", \"%ps\") = %zu\n", c3, c1, ret);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_metai_match(void)
+{
+ bool ret;
+ const u16 c[] = L"Das U-Boot";
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"*");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[rstu] U-Boot");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[q-v] U-Boot");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da? U-Boot");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"D*Bo*t");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[xyz] U-Boot");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[a-d] U-Boot");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da?? U-Boot");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"D*Bo*tt");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_str_lwr(void)
+{
+ u16 c[] = L"U-Boot";
+
+ unicode_collation_protocol->str_lwr(unicode_collation_protocol, c);
+ if (efi_st_strcmp_16_8(c, "u-boot")) {
+ efi_st_error("str_lwr returned \"%ps\"\n", c);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_str_upr(void)
+{
+ u16 c[] = L"U-Boot";
+
+ unicode_collation_protocol->str_upr(unicode_collation_protocol, c);
+ if (efi_st_strcmp_16_8(c, "U-BOOT")) {
+ efi_st_error("str_lwr returned \"%ps\"\n", c);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_fat_to_str(void)
+{
+ u16 str[16];
+
+ boottime->set_mem(str, sizeof(str), 0);
+ unicode_collation_protocol->fat_to_str(unicode_collation_protocol, 6,
+ "U-BOOT", str);
+ if (efi_st_strcmp_16_8(str, "U-BOOT")) {
+ efi_st_error("fat_to_str returned \"%ps\"\n", str);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_str_to_fat(void)
+{
+ char fat[16];
+ bool ret;
+
+ boottime->set_mem(fat, sizeof(fat), 0);
+ ret = unicode_collation_protocol->str_to_fat(unicode_collation_protocol,
+ L"U -Boo.t", 6, fat);
+ if (ret || efi_st_strcmp_16_8(L"U-BOOT", fat)) {
+ efi_st_error("str_to_fat returned %u, \"%s\"\n", ret, fat);
+ return EFI_ST_FAILURE;
+ }
+
+ boottime->set_mem(fat, 16, 0);
+ ret = unicode_collation_protocol->str_to_fat(unicode_collation_protocol,
+ L"U\\Boot", 6, fat);
+ if (!ret || efi_st_strcmp_16_8(L"U_BOOT", fat)) {
+ efi_st_error("str_to_fat returned %u, \"%s\"\n", ret, fat);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/**
+ * execute() - Execute unit test.
+ *
+ * ReturnValue: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ int ret;
+
+ if (!unicode_collation_protocol) {
+ efi_st_printf("Unicode collation protocol missing\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = test_stri_coll();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_metai_match();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_str_lwr();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_str_upr();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_fat_to_str();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_str_to_fat();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(unicoll) = {
+ .name = "unicode collation",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .execute = execute,
+ .setup = setup,
+};
diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c
index 87a04f8..96a964c 100644
--- a/lib/efi_selftest/efi_selftest_util.c
+++ b/lib/efi_selftest/efi_selftest_util.c
@@ -9,6 +9,99 @@
#include <efi_selftest.h>
+struct efi_st_translate {
+ u16 code;
+ u16 *text;
+};
+
+static struct efi_st_translate efi_st_control_characters[] = {
+ {0, L"Null"},
+ {8, L"BS"},
+ {9, L"TAB"},
+ {10, L"LF"},
+ {13, L"CR"},
+ {0, NULL},
+};
+
+static u16 efi_st_ch[] = L"' '";
+static u16 efi_st_unknown[] = L"unknown";
+
+static struct efi_st_translate efi_st_scan_codes[] = {
+ {0x00, L"Null"},
+ {0x01, L"Up"},
+ {0x02, L"Down"},
+ {0x03, L"Right"},
+ {0x04, L"Left"},
+ {0x05, L"Home"},
+ {0x06, L"End"},
+ {0x07, L"Insert"},
+ {0x08, L"Delete"},
+ {0x09, L"Page Up"},
+ {0x0a, L"Page Down"},
+ {0x0b, L"FN 1"},
+ {0x0c, L"FN 2"},
+ {0x0d, L"FN 3"},
+ {0x0e, L"FN 4"},
+ {0x0f, L"FN 5"},
+ {0x10, L"FN 6"},
+ {0x11, L"FN 7"},
+ {0x12, L"FN 8"},
+ {0x13, L"FN 9"},
+ {0x14, L"FN 10"},
+ {0x15, L"FN 11"},
+ {0x16, L"FN 12"},
+ {0x17, L"Escape"},
+ {0x68, L"FN 13"},
+ {0x69, L"FN 14"},
+ {0x6a, L"FN 15"},
+ {0x6b, L"FN 16"},
+ {0x6c, L"FN 17"},
+ {0x6d, L"FN 18"},
+ {0x6e, L"FN 19"},
+ {0x6f, L"FN 20"},
+ {0x70, L"FN 21"},
+ {0x71, L"FN 22"},
+ {0x72, L"FN 23"},
+ {0x73, L"FN 24"},
+ {0x7f, L"Mute"},
+ {0x80, L"Volume Up"},
+ {0x81, L"Volume Down"},
+ {0x100, L"Brightness Up"},
+ {0x101, L"Brightness Down"},
+ {0x102, L"Suspend"},
+ {0x103, L"Hibernate"},
+ {0x104, L"Toggle Display"},
+ {0x105, L"Recovery"},
+ {0x106, L"Reject"},
+ {0x0, NULL},
+};
+
+u16 *efi_st_translate_char(u16 code)
+{
+ struct efi_st_translate *tr;
+
+ if (code >= ' ') {
+ efi_st_ch[1] = code;
+ return efi_st_ch;
+ }
+ for (tr = efi_st_control_characters; tr->text; ++tr) {
+ if (tr->code == code)
+ return tr->text;
+ }
+ return efi_st_unknown;
+}
+
+u16 *efi_st_translate_code(u16 code)
+{
+ struct efi_st_translate *tr;
+
+ for (tr = efi_st_scan_codes; tr->text; ++tr) {
+ if (tr->code == code)
+ return tr->text;
+ }
+ return efi_st_unknown;
+}
+
int efi_st_memcmp(const void *buf1, const void *buf2, size_t length)
{
const u8 *pos1 = buf1;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 914fbd3..4213441 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -274,28 +274,23 @@ static char *string(char *buf, char *end, char *s, int field_width,
return buf;
}
+/* U-Boot uses UTF-16 strings in the EFI context only. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
{
u16 *str = s ? s : L"<NULL>";
- int utf16_len = utf16_strnlen(str, precision);
- u8 utf8[utf16_len * MAX_UTF8_PER_UTF16];
- int utf8_len, i;
-
- utf8_len = utf16_to_utf8(utf8, str, utf16_len) - utf8;
+ ssize_t len = utf16_strnlen(str, precision);
if (!(flags & LEFT))
- while (utf8_len < field_width--)
+ for (; len < field_width; --field_width)
ADDCH(buf, ' ');
- for (i = 0; i < utf8_len; ++i)
- ADDCH(buf, utf8[i]);
- while (utf8_len < field_width--)
+ utf16_utf8_strncpy(&buf, str, len);
+ for (; len < field_width; --field_width)
ADDCH(buf, ' ');
return buf;
}
-#if defined(CONFIG_EFI_LOADER) && \
- !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
static char *device_path_string(char *buf, char *end, void *dp, int field_width,
int precision, int flags)
{
@@ -450,8 +445,8 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
#endif
switch (*fmt) {
-#if defined(CONFIG_EFI_LOADER) && \
- !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
+/* Device paths only exist in the EFI context. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
case 'D':
return device_path_string(buf, end, ptr, field_width,
precision, flags);
@@ -612,10 +607,14 @@ repeat:
continue;
case 's':
- if (qualifier == 'l' && !IS_ENABLED(CONFIG_SPL_BUILD)) {
+/* U-Boot uses UTF-16 strings in the EFI context only. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+ if (qualifier == 'l') {
str = string16(str, end, va_arg(args, u16 *),
field_width, precision, flags);
- } else {
+ } else
+#endif
+ {
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
}
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
index 81ba550..03e4d28 100644
--- a/scripts/config_whitelist.txt
+++ b/scripts/config_whitelist.txt
@@ -4069,7 +4069,6 @@ CONFIG_SYS_RSTC_RMR_VAL
CONFIG_SYS_RTC_BUS_NUM
CONFIG_SYS_RTC_CNT
CONFIG_SYS_RTC_OSCILLATOR
-CONFIG_SYS_RTC_PL031_BASE
CONFIG_SYS_RTC_REG_BASE_ADDR
CONFIG_SYS_RTC_SETUP
CONFIG_SYS_RV3029_TCR
diff --git a/test/Kconfig b/test/Kconfig
index 3643761..de16d17 100644
--- a/test/Kconfig
+++ b/test/Kconfig
@@ -15,6 +15,14 @@ config UT_TIME
problems. But if you are having problems with udelay() and the like,
this is a good place to start.
+config UT_UNICODE
+ bool "Unit tests for Unicode functions"
+ depends on UNIT_TEST
+ default y
+ help
+ Enables the 'ut unicode' command which tests that the functions for
+ manipulating Unicode strings work correctly.
+
source "test/dm/Kconfig"
source "test/env/Kconfig"
source "test/overlay/Kconfig"
diff --git a/test/Makefile b/test/Makefile
index 1092011..a5f52fd 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_SANDBOX) += command_ut.o
obj-$(CONFIG_SANDBOX) += compression.o
obj-$(CONFIG_SANDBOX) += print_ut.o
obj-$(CONFIG_UT_TIME) += time_ut.o
+obj-$(CONFIG_UT_UNICODE) += unicode_ut.o
obj-$(CONFIG_$(SPL_)LOG) += log/
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 934a5a9..b7e01a4 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -49,6 +49,9 @@ static cmd_tbl_t cmd_ut_sub[] = {
#ifdef CONFIG_UT_TIME
U_BOOT_CMD_MKENT(time, CONFIG_SYS_MAXARGS, 1, do_ut_time, "", ""),
#endif
+#if CONFIG_IS_ENABLED(UT_UNICODE) && !defined(API_BUILD)
+ U_BOOT_CMD_MKENT(unicode, CONFIG_SYS_MAXARGS, 1, do_ut_unicode, "", ""),
+#endif
#ifdef CONFIG_SANDBOX
U_BOOT_CMD_MKENT(compression, CONFIG_SYS_MAXARGS, 1, do_ut_compression,
"", ""),
@@ -93,6 +96,9 @@ static int do_ut(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
#ifdef CONFIG_SYS_LONGHELP
static char ut_help_text[] =
"all - execute all enabled tests\n"
+#ifdef CONFIG_SANDBOX
+ "ut compression - Test compressors and bootm decompression\n"
+#endif
#ifdef CONFIG_UT_DM
"ut dm [test-name]\n"
#endif
@@ -105,11 +111,12 @@ static char ut_help_text[] =
#ifdef CONFIG_UT_TIME
"ut time - Very basic test of time functions\n"
#endif
-#ifdef CONFIG_SANDBOX
- "ut compression - Test compressors and bootm decompression\n"
+#if defined(CONFIG_UT_UNICODE) && \
+ !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
+ "ut unicode [test-name] - test Unicode functions\n"
#endif
;
-#endif
+#endif /* CONFIG_SYS_LONGHELP */
U_BOOT_CMD(
ut, CONFIG_SYS_MAXARGS, 1, do_ut,
diff --git a/test/fs/fs-test.sh b/test/fs/fs-test.sh
index 9482239..86308cf 100755
--- a/test/fs/fs-test.sh
+++ b/test/fs/fs-test.sh
@@ -7,18 +7,20 @@
# It currently tests the fs/sb and native commands for ext4 and fat partitions
# Expected results are as follows:
# EXT4 tests:
-# fs-test.sb.ext4.out: Summary: PASS: 24 FAIL: 0
-# fs-test.ext4.out: Summary: PASS: 24 FAIL: 0
-# fs-test.fs.ext4.out: Summary: PASS: 24 FAIL: 0
+# fs-test.sb.ext4 Summary: PASS: 24 FAIL: 0
+# fs-test.nonfs.ext4 Summary: PASS: 24 FAIL: 0
+# fs-test.fs.ext4 Summary: PASS: 24 FAIL: 0
# FAT16 tests:
-# fs-test.sb.fat16.out: Summary: PASS: 24 FAIL: 0
-# fs-test.fat16.out: Summary: PASS: 20 FAIL: 4
-# fs-test.fs.fat16.out: Summary: PASS: 20 FAIL: 4
+# fs-test.sb.fat16 Summary: PASS: 24 FAIL: 0
+# fs-test.nonfs.fat16 Summary: PASS: 24 FAIL: 0
+# fs-test.fs.fat16 Summary: PASS: 24 FAIL: 0
# FAT32 tests:
-# fs-test.sb.fat32.out: Summary: PASS: 24 FAIL: 0
-# fs-test.fat32.out: Summary: PASS: 20 FAIL: 4
-# fs-test.fs.fat32.out: Summary: PASS: 20 FAIL: 4
-# Total Summary: TOTAL PASS: 200 TOTAL FAIL: 16
+# fs-test.sb.fat32 Summary: PASS: 24 FAIL: 0
+# fs-test.nonfs.fat32 Summary: PASS: 24 FAIL: 0
+# fs-test.fs.fat32 Summary: PASS: 24 FAIL: 0
+# --------------------------------------------
+# Total Summary: TOTAL PASS: 216 TOTAL FAIL: 0
+# --------------------------------------------
# pre-requisite binaries list.
PREREQ_BINS="md5sum mkfs mount umount dd fallocate mkdir"
@@ -522,7 +524,7 @@ function check_results() {
"TC11: 1MB write to $3.w - content verified"
# Check lookup of 'dot' directory
- grep -A4 "Test Case 12 " "$1" | grep -q 'Unable to write file'
+ grep -A4 "Test Case 12 " "$1" | grep -q 'Unable to write'
pass_fail "TC12: 1MB write to . - write denied"
# Check directory traversal
diff --git a/test/print_ut.c b/test/print_ut.c
index fb46db8..f0f1d60 100644
--- a/test/print_ut.c
+++ b/test/print_ut.c
@@ -6,8 +6,7 @@
#define DEBUG
#include <common.h>
-#if defined(CONFIG_EFI_LOADER) && \
- !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
#include <efi_api.h>
#endif
#include <display_options.h>
@@ -19,8 +18,7 @@
/* Test efi_loader specific printing */
static void efi_ut_print(void)
{
-#if defined(CONFIG_EFI_LOADER) && \
- !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
char str[10];
u8 buf[sizeof(struct efi_device_path_sd_mmc_path) +
sizeof(struct efi_device_path)];
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py
index 747d52d..e0833ff 100644
--- a/test/py/tests/test_efi_selftest.py
+++ b/test/py/tests/test_efi_selftest.py
@@ -16,7 +16,7 @@ def test_efi_selftest(u_boot_console):
u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
if m != 0:
- raise Exception('Failures occured during the EFI selftest')
+ raise Exception('Failures occurred during the EFI selftest')
u_boot_console.run_command(cmd='', wait_for_echo=False, wait_for_prompt=False);
m = u_boot_console.p.expect(['resetting', 'U-Boot'])
if m != 0:
@@ -48,3 +48,152 @@ def test_efi_selftest_watchdog_reboot(u_boot_console):
if m != 0:
raise Exception('Reset failed in \'watchdog reboot\' test')
u_boot_console.restart_uboot();
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_text_input(u_boot_console):
+ """Test the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+
+ :param u_boot_console: U-Boot console
+
+ This function calls the text input EFI selftest.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest text input')
+ output = u_boot_console.run_command(cmd='bootefi selftest',
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(['To terminate type \'x\''])
+ if m != 0:
+ raise Exception('No prompt for \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.p.timeout = 500
+ # EOT
+ u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 4 \(unknown\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('EOT failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # BS
+ u_boot_console.run_command(cmd=chr(8), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 8 \(BS\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # TAB
+ u_boot_console.run_command(cmd=chr(9), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 9 \(TAB\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # a
+ u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 97 \(\'a\'\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('\'a\' failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # UP escape sequence
+ u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 0 \(Null\), scan code 1 \(Up\)'])
+ if m != 0:
+ raise Exception('UP failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # Euro sign
+ u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Unicode char 8364 \(\''])
+ if m != 0:
+ raise Exception('Euro sign failed in \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
+ if m != 0:
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot();
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_text_input_ex(u_boot_console):
+ """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
+
+ :param u_boot_console: U-Boot console
+
+ This function calls the extended text input EFI selftest.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest extended text input')
+ output = u_boot_console.run_command(cmd='bootefi selftest',
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(['To terminate type \'CTRL\+x\''])
+ if m != 0:
+ raise Exception('No prompt for \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.p.timeout = 500
+ # EOT
+ u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 4 \(unknown\), scan code 0 \(CTRL\+Null\)'])
+ if m != 0:
+ raise Exception('EOT failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # BS
+ u_boot_console.run_command(cmd=chr(8), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 8 \(BS\), scan code 0 \(\+Null\)'])
+ if m != 0:
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # TAB
+ u_boot_console.run_command(cmd=chr(9), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 9 \(TAB\), scan code 0 \(\+Null\)'])
+ if m != 0:
+ raise Exception('TAB failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # a
+ u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 97 \(\'a\'\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('\'a\' failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # UP escape sequence
+ u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 0 \(Null\), scan code 1 \(\+Up\)'])
+ if m != 0:
+ raise Exception('UP failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # Euro sign
+ u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Unicode char 8364 \(\''])
+ if m != 0:
+ raise Exception('Euro sign failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # SHIFT+ALT+FN 5
+ u_boot_console.run_command(cmd='\x1b\x5b\x31\x35\x3b\x34\x7e',
+ wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ ['Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)'])
+ if m != 0:
+ raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
+ if m != 0:
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot();
diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py
new file mode 100644
index 0000000..6404b31
--- /dev/null
+++ b/test/py/tests/test_fs/conftest.py
@@ -0,0 +1,392 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import pytest
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+from fstest_defs import *
+
+supported_fs_basic = ['fat16', 'fat32', 'ext4']
+supported_fs_ext = ['fat16', 'fat32']
+supported_fs_mkdir = ['fat16', 'fat32']
+supported_fs_unlink = ['fat16', 'fat32']
+
+#
+# Filesystem test specific setup
+#
+def pytest_addoption(parser):
+ parser.addoption('--fs-type', action='append', default=None,
+ help='Targeting Filesystem Types')
+
+def pytest_configure(config):
+ global supported_fs_basic
+ global supported_fs_ext
+ global supported_fs_mkdir
+ global supported_fs_unlink
+
+ def intersect(listA, listB):
+ return [x for x in listA if x in listB]
+
+ supported_fs = config.getoption('fs_type')
+ if supported_fs:
+ print("*** FS TYPE modified: %s" % supported_fs)
+ supported_fs_basic = intersect(supported_fs, supported_fs_basic)
+ supported_fs_ext = intersect(supported_fs, supported_fs_ext)
+ supported_fs_mkdir = intersect(supported_fs, supported_fs_mkdir)
+ supported_fs_unlink = intersect(supported_fs, supported_fs_unlink)
+
+def pytest_generate_tests(metafunc):
+ if 'fs_obj_basic' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_basic', supported_fs_basic,
+ indirect=True, scope='module')
+ if 'fs_obj_ext' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_ext', supported_fs_ext,
+ indirect=True, scope='module')
+ if 'fs_obj_mkdir' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_mkdir', supported_fs_mkdir,
+ indirect=True, scope='module')
+ if 'fs_obj_unlink' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_unlink', supported_fs_unlink,
+ indirect=True, scope='module')
+
+#
+# Helper functions
+#
+def fstype_to_ubname(fs_type):
+ if re.match('fat', fs_type):
+ return 'fat'
+ else:
+ return fs_type
+
+def check_ubconfig(config, fs_type):
+ if not config.buildconfig.get('config_cmd_%s' % fs_type, None):
+ pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper())
+ if not config.buildconfig.get('config_%s_write' % fs_type, None):
+ pytest.skip('.config feature "%s_WRITE" not enabled'
+ % fs_type.upper())
+
+def mk_fs(config, fs_type, size, id):
+ fs_img = '%s.%s.img' % (id, fs_type)
+ fs_img = config.persistent_data_dir + '/' + fs_img
+
+ if fs_type == 'fat16':
+ mkfs_opt = '-F 16'
+ elif fs_type == 'fat32':
+ mkfs_opt = '-F 32'
+ else:
+ mkfs_opt = ''
+
+ if re.match('fat', fs_type):
+ fs_lnxtype = 'vfat'
+ else:
+ fs_lnxtype = fs_type
+
+ count = (size + 1048576 - 1) / 1048576
+
+ try:
+ check_call('rm -f %s' % fs_img, shell=True)
+ check_call('dd if=/dev/zero of=%s bs=1M count=%d'
+ % (fs_img, count), shell=True)
+ check_call('mkfs.%s %s %s'
+ % (fs_lnxtype, mkfs_opt, fs_img), shell=True)
+ return fs_img
+ except CalledProcessError:
+ call('rm -f %s' % fs_img, shell=True)
+ raise
+
+# from test/py/conftest.py
+def tool_is_in_path(tool):
+ for path in os.environ["PATH"].split(os.pathsep):
+ fn = os.path.join(path, tool)
+ if os.path.isfile(fn) and os.access(fn, os.X_OK):
+ return True
+ return False
+
+fuse_mounted = False
+
+def mount_fs(fs_type, device, mount_point):
+ global fuse_mounted
+
+ fuse_mounted = False
+ try:
+ if tool_is_in_path('guestmount'):
+ fuse_mounted = True
+ check_call('guestmount -a %s -m /dev/sda %s'
+ % (device, mount_point), shell=True)
+ else:
+ mount_opt = "loop,rw"
+ if re.match('fat', fs_type):
+ mount_opt += ",umask=0000"
+
+ check_call('sudo mount -o %s %s %s'
+ % (mount_opt, device, mount_point), shell=True)
+
+ # may not be effective for some file systems
+ check_call('sudo chmod a+rw %s' % mount_point, shell=True)
+ except CalledProcessError:
+ raise
+
+def umount_fs(fs_type, mount_point):
+ if fuse_mounted:
+ call('sync')
+ call('guestunmount %s' % mount_point, shell=True)
+ else:
+ call('sudo umount %s' % mount_point, shell=True)
+
+#
+# Fixture for basic fs test
+# derived from test/fs/fs-test.sh
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_basic(request, u_boot_config):
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ small_file = mount_dir + '/' + SMALL_FILE
+ big_file = mount_dir + '/' + BIG_FILE
+
+ try:
+
+ # 3GiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
+
+ # Mount the image so we can populate it.
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ mount_fs(fs_type, fs_img, mount_dir)
+
+ # Create a subdirectory.
+ check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
+
+ # Create big file in this image.
+ # Note that we work only on the start 1MB, couple MBs in the 2GB range
+ # and the last 1 MB of the huge 2.5GB file.
+ # So, just put random values only in those areas.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % big_file, shell=True)
+ check_call('dd if=/dev/urandom of=%s bs=1M count=2 seek=2047'
+ % big_file, shell=True)
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1 seek=2499'
+ % big_file, shell=True)
+
+ # Create a small file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % small_file, shell=True)
+
+ # Delete the small file copies which possibly are written as part of a
+ # previous test.
+ # check_call('rm -f "%s.w"' % MB1, shell=True)
+ # check_call('rm -f "%s.w2"' % MB1, shell=True)
+
+ # Generate the md5sums of reads that we will test against small file
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % small_file, shell=True)
+ md5val = [ out.split()[0] ]
+
+ # Generate the md5sums of reads that we will test against big file
+ # One from beginning of file.
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True)
+ md5val.append(out.split()[0])
+
+ # One from end of file.
+ out = check_output(
+ 'dd if=%s bs=1M skip=2499 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True)
+ md5val.append(out.split()[0])
+
+ # One from the last 1MB chunk of 2GB
+ out = check_output(
+ 'dd if=%s bs=1M skip=2047 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True)
+ md5val.append(out.split()[0])
+
+ # One from the start 1MB chunk from 2GB
+ out = check_output(
+ 'dd if=%s bs=1M skip=2048 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True)
+ md5val.append(out.split()[0])
+
+ # One 1MB chunk crossing the 2GB boundary
+ out = check_output(
+ 'dd if=%s bs=512K skip=4095 count=2 2> /dev/null | md5sum'
+ % big_file, shell=True)
+ md5val.append(out.split()[0])
+
+ umount_fs(fs_type, mount_dir)
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ return
+ else:
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ umount_fs(fs_type, mount_dir)
+ call('rmdir %s' % mount_dir, shell=True)
+ if fs_img:
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for extended fs test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_ext(request, u_boot_config):
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ min_file = mount_dir + '/' + MIN_FILE
+ tmp_file = mount_dir + '/tmpfile'
+
+ try:
+
+ # 128MiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+
+ # Mount the image so we can populate it.
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ mount_fs(fs_type, fs_img, mount_dir)
+
+ # Create a test directory
+ check_call('mkdir %s/dir1' % mount_dir, shell=True)
+
+ # Create a small file and calculate md5
+ check_call('dd if=/dev/urandom of=%s bs=1K count=20'
+ % min_file, shell=True)
+ out = check_output(
+ 'dd if=%s bs=1K 2> /dev/null | md5sum'
+ % min_file, shell=True)
+ md5val = [ out.split()[0] ]
+
+ # Calculate md5sum of Test Case 4
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=5 count=20'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True)
+ md5val.append(out.split()[0])
+
+ # Calculate md5sum of Test Case 5
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=5 count=5'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True)
+ md5val.append(out.split()[0])
+
+ # Calculate md5sum of Test Case 7
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=20 count=20'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True)
+ md5val.append(out.split()[0])
+
+ check_call('rm %s' % tmp_file, shell=True)
+ umount_fs(fs_type, mount_dir)
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ return
+ else:
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ umount_fs(fs_type, mount_dir)
+ call('rmdir %s' % mount_dir, shell=True)
+ if fs_img:
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for mkdir test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_mkdir(request, u_boot_config):
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ try:
+ # 128MiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ else:
+ yield [fs_ubtype, fs_img]
+ finally:
+ if fs_img:
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for unlink test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_unlink(request, u_boot_config):
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ try:
+
+ # 128MiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+
+ # Mount the image so we can populate it.
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ mount_fs(fs_type, fs_img, mount_dir)
+
+ # Test Case 1 & 3
+ check_call('mkdir %s/dir1' % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir1/file1 bs=1K count=1'
+ % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir1/file2 bs=1K count=1'
+ % mount_dir, shell=True)
+
+ # Test Case 2
+ check_call('mkdir %s/dir2' % mount_dir, shell=True)
+ for i in range(0, 20):
+ check_call('mkdir %s/dir2/0123456789abcdef%02x'
+ % (mount_dir, i), shell=True)
+
+ # Test Case 4
+ check_call('mkdir %s/dir4' % mount_dir, shell=True)
+
+ # Test Case 5, 6 & 7
+ check_call('mkdir %s/dir5' % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1'
+ % mount_dir, shell=True)
+
+ umount_fs(fs_type, mount_dir)
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ return
+ else:
+ yield [fs_ubtype, fs_img]
+ finally:
+ umount_fs(fs_type, mount_dir)
+ call('rmdir %s' % mount_dir, shell=True)
+ if fs_img:
+ call('rm -f %s' % fs_img, shell=True)
diff --git a/test/py/tests/test_fs/fstest_defs.py b/test/py/tests/test_fs/fstest_defs.py
new file mode 100644
index 0000000..5f10756
--- /dev/null
+++ b/test/py/tests/test_fs/fstest_defs.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+# $MIN_FILE is the name of the 20KB file in the file system image
+MIN_FILE='testfile'
+
+# $SMALL_FILE is the name of the 1MB file in the file system image
+SMALL_FILE='1MB.file'
+
+# $BIG_FILE is the name of the 2.5GB file in the file system image
+BIG_FILE='2.5GB.file'
+
+ADDR=0x01000008
+LENGTH=0x00100000
diff --git a/test/py/tests/test_fs/test_basic.py b/test/py/tests/test_fs/test_basic.py
new file mode 100644
index 0000000..c067cc9
--- /dev/null
+++ b/test/py/tests/test_fs/test_basic.py
@@ -0,0 +1,287 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:Basic Test
+
+"""
+This test verifies basic read/write operation on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+
+@pytest.mark.boardspec('sandbox')
+class TestFsBasic(object):
+ def test_fs1(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 1 - ls command, listing a root directory and invalid directory
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 1a - ls'):
+ # Test Case 1 - ls
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sls host 0:0' % fs_type])
+ assert(re.search('2621440000 *%s' % BIG_FILE, ''.join(output)))
+ assert(re.search('1048576 *%s' % SMALL_FILE, ''.join(output)))
+
+ with u_boot_console.log.section('Test Case 1b - ls (invalid dir)'):
+ # In addition, test with a nonexistent directory to see if we crash.
+ output = u_boot_console.run_command(
+ '%sls host 0:0 invalid_d' % fs_type)
+ if fs_type == 'ext4':
+ assert('Can not find directory' in output)
+ else:
+ assert('' == output)
+
+ def test_fs2(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 2 - size command for a small file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 2a - size (small)'):
+ # 1MB is 0x0010 0000
+ # Test Case 2a - size of small file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%ssize host 0:0 /%s' % (fs_type, SMALL_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ with u_boot_console.log.section('Test Case 2b - size (/../<file>)'):
+ # Test Case 2b - size of small file via a path using '..'
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /SUBDIR/../%s' % (fs_type, SMALL_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ def test_fs3(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 3 - size command for a large file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 3 - size (large)'):
+ # 2.5GB (1024*1024*2500) is 0x9C40 0000
+ # Test Case 3 - size of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%ssize host 0:0 /%s' % (fs_type, BIG_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=9c400000' in ''.join(output))
+
+ def test_fs4(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 4 - load a small file, 1MB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 4 - load (small)'):
+ # Test Case 4a - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ def test_fs5(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 5 - load, reading first 1MB of 3GB file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 5 - load (first 1MB)'):
+ # Test Case 5a - First 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x0' % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 5b - First 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+
+ def test_fs6(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 6 - load, reading last 1MB of 3GB file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 6 - load (last 1MB)'):
+ # fails for ext as no offset support
+ # Test Case 6a - Last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x9c300000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 6b - Last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[2] in ''.join(output))
+
+ def test_fs7(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 7 - load, 1MB from the last 1MB in 2GB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 7 - load (last 1MB in 2GB)'):
+ # fails for ext as no offset support
+ # Test Case 7a - One from the last 1MB chunk of 2GB
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x7ff00000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 7b - One from the last 1MB chunk of 2GB
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[3] in ''.join(output))
+
+ def test_fs8(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 8 - load, reading first 1MB in 2GB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 8 - load (first 1MB in 2GB)'):
+ # fails for ext as no offset support
+ # Test Case 8a - One from the start 1MB chunk from 2GB
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x80000000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 8b - One from the start 1MB chunk from 2GB
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[4] in ''.join(output))
+
+ def test_fs9(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 9 - load, 1MB crossing 2GB boundary
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 9 - load (crossing 2GB boundary)'):
+ # fails for ext as no offset support
+ # Test Case 9a - One 1MB chunk crossing the 2GB boundary
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x7ff80000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 9b - One 1MB chunk crossing the 2GB boundary
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[5] in ''.join(output))
+
+ def test_fs10(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 10 - load, reading beyond file end'):
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 10 - load (beyond file end)'):
+ # Generic failure case
+ # Test Case 10 - 2MB chunk from the last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s 0x00200000 0x9c300000'
+ % (fs_type, ADDR, BIG_FILE),
+ 'printenv filesize',
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ def test_fs11(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 11 - write'
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 11 - write'):
+ # Read 1MB from small file
+ # Write it back to test the writes
+ # Test Case 11a - Check that the write succeeded
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ '%swrite host 0:0 %x /%s.w $filesize'
+ % (fs_type, ADDR, SMALL_FILE)])
+ assert('1048576 bytes written' in ''.join(output))
+
+ # Test Case 11b - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.w' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ def test_fs12(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 12 - write to "." directory
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 12 - write (".")'):
+ # Next test case checks writing a file whose dirent
+ # is the first in the block, which is always true for "."
+ # The write should fail, but the lookup should work
+ # Test Case 12 - Check directory traversal
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%swrite host 0:0 %x /. 0x10' % (fs_type, ADDR)])
+ assert('Unable to write' in ''.join(output))
+
+ def test_fs13(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 13 - write to a file with "/./<filename>"
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 13 - write ("./<file>")'):
+ # Read 1MB from small file
+ # Write it via "same directory", i.e. "." dirent
+ # Test Case 13a - Check directory traversal
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ '%swrite host 0:0 %x /./%s2 $filesize'
+ % (fs_type, ADDR, SMALL_FILE)])
+ assert('1048576 bytes written' in ''.join(output))
+
+ # Test Case 13b - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /./%s2' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ # Test Case 13c - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /%s2' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
diff --git a/test/py/tests/test_fs/test_ext.py b/test/py/tests/test_fs/test_ext.py
new file mode 100644
index 0000000..38217d0
--- /dev/null
+++ b/test/py/tests/test_fs/test_ext.py
@@ -0,0 +1,224 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:Exntented Test
+
+"""
+This test verifies extended write operation on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+
+@pytest.mark.boardspec('sandbox')
+class TestFsExt(object):
+ def test_fs_ext1(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 1 - write a file with absolute path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 1 - write with abs path'):
+ # Test Case 1a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w1 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('20480 bytes written' in ''.join(output))
+
+ # Test Case 1b - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w1' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ def test_fs_ext2(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 2 - write to a file with relative path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 2 - write with rel path'):
+ # Test Case 2a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x dir1/%s.w2 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('20480 bytes written' in ''.join(output))
+
+ # Test Case 2b - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x dir1/%s.w2' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ def test_fs_ext3(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 3 - write to a file with invalid path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 3 - write with invalid path'):
+ # Test Case 3 - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/none/%s.w3 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('Unable to write "/dir1/none/' in ''.join(output))
+
+ def test_fs_ext4(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 4 - write at non-zero offset, enlarging file size
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 4 - write at non-zero offset, enlarging file size'):
+ # Test Case 4a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w4 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w4 $filesize 0x1400'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('20480 bytes written' in output)
+
+ # Test Case 4b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w4' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=6400' in ''.join(output))
+
+ # Test Case 4c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w4' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+
+ def test_fs_ext5(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 5 - write at non-zero offset, shrinking file size
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 5 - write at non-zero offset, shrinking file size'):
+ # Test Case 5a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w5 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w5 0x1400 0x1400'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('5120 bytes written' in output)
+
+ # Test Case 5b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w5' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=2800' in ''.join(output))
+
+ # Test Case 5c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w5' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[2] in ''.join(output))
+
+ def test_fs_ext6(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 6 - write nothing at the start, truncating to zero
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 6 - write nothing at the start, truncating to zero'):
+ # Test Case 6a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w6 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w6 0 0'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('0 bytes written' in output)
+
+ # Test Case 6b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w6' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=0' in ''.join(output))
+
+ def test_fs_ext7(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 7 - write at the end (append)
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 7 - write at the end (append)'):
+ # Test Case 7a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w7 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w7 $filesize $filesize'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('20480 bytes written' in output)
+
+ # Test Case 7b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w7' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=a000' in ''.join(output))
+
+ # Test Case 7c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w7' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[3] in ''.join(output))
+
+ def test_fs_ext8(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 8 - write at offset beyond the end of file
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 8 - write beyond the end'):
+ # Test Case 8a - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w8 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w8 0x1400 %x'
+ % (fs_type, ADDR, MIN_FILE, 0x100000 + 0x1400))
+ assert('Unable to write "/dir1' in output)
+
+ def test_fs_ext9(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 9 - write to a non-existing file at non-zero offset
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 9 - write to non-existing file with non-zero offset'):
+ # Test Case 9a - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w9 0x1400 0x1400'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('Unable to write "/dir1' in ''.join(output))
diff --git a/test/py/tests/test_fs/test_mkdir.py b/test/py/tests/test_fs/test_mkdir.py
new file mode 100644
index 0000000..d9da97b
--- /dev/null
+++ b/test/py/tests/test_fs/test_mkdir.py
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:mkdir Test
+
+"""
+This test verifies mkdir operation on file system.
+"""
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+class TestMkdir(object):
+ def test_mkdir1(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 1 - create a directory under a root
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 1 - mkdir'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir1' % fs_type,
+ '%sls host 0:0 /' % fs_type])
+ assert('dir1/' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+
+ def test_mkdir2(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 2 - create a directory under a sub-directory
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 2 - mkdir (sub-sub directory)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir1/dir2' % fs_type,
+ '%sls host 0:0 dir1' % fs_type])
+ assert('dir2/' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1/dir2' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+
+ def test_mkdir3(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 3 - trying to create a directory with a non-existing
+ path should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 3 - mkdir (non-existing path)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 none/dir3' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+
+ def test_mkdir4(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 4 - trying to create "." should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 4 - mkdir (".")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 .' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+
+ def test_mkdir5(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 5 - trying to create ".." should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 5 - mkdir ("..")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 ..' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+
+ def test_mkdir6(self, u_boot_console, fs_obj_mkdir):
+ """
+ 'Test Case 6 - create as many directories as amount of directory
+ entries goes beyond a cluster size)'
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 6 - mkdir (create many)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir6' % fs_type,
+ '%sls host 0:0 /' % fs_type])
+ assert('dir6/' in ''.join(output))
+
+ for i in range(0, 20):
+ output = u_boot_console.run_command(
+ '%smkdir host 0:0 dir6/0123456789abcdef%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 dir6' % fs_type)
+ assert('0123456789abcdef00/' in output)
+ assert('0123456789abcdef13/' in output)
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir6/0123456789abcdef13/.' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir6/0123456789abcdef13/..' % fs_type)
+ assert('0123456789abcdef00/' in output)
+ assert('0123456789abcdef13/' in output)
diff --git a/test/py/tests/test_fs/test_unlink.py b/test/py/tests/test_fs/test_unlink.py
new file mode 100644
index 0000000..69c1a6e
--- /dev/null
+++ b/test/py/tests/test_fs/test_unlink.py
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:unlink Test
+
+"""
+This test verifies unlink operation (deleting a file or a directory)
+on file system.
+"""
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+class TestUnlink(object):
+ def test_unlink1(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 1 - delete a file
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 1 - unlink (file)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir1/file1' % fs_type,
+ '%sls host 0:0 dir1/file1' % fs_type])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1/' % fs_type)
+ assert(not 'file1' in output)
+ assert('file2' in output)
+
+ def test_unlink2(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 2 - delete many files
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 2 - unlink (many)'):
+ output = u_boot_console.run_command('host bind 0 %s' % fs_img)
+
+ for i in range(0, 20):
+ output = u_boot_console.run_command_list([
+ '%srm host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i),
+ '%sls host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i)])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir2' % fs_type)
+ assert('0 file(s), 2 dir(s)' in output)
+
+ def test_unlink3(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 3 - trying to delete a non-existing file should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 3 - unlink (non-existing)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir1/nofile' % fs_type])
+ assert('nofile: doesn\'t exist' in ''.join(output))
+
+ def test_unlink4(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 4 - delete an empty directory
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 4 - unlink (directory)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir4' % fs_type])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 /' % fs_type)
+ assert(not 'dir4' in output)
+
+ def test_unlink5(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 5 - trying to deleting a non-empty directory ".."
+ should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 5 - unlink ("non-empty directory")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+
+ def test_unlink6(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 6 - trying to deleting a "." should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 6 - unlink (".")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5/.' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+
+ def test_unlink7(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 7 - trying to deleting a ".." should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 7 - unlink ("..")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5/..' % fs_type])
+ assert('directory is not empty' in ''.join(output))
diff --git a/test/unicode_ut.c b/test/unicode_ut.c
new file mode 100644
index 0000000..b115d18
--- /dev/null
+++ b/test/unicode_ut.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Unit tests for Unicode functions
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <command.h>
+#include <errno.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+/* Linker list entry for a Unicode test */
+#define UNICODE_TEST(_name) UNIT_TEST(_name, 0, unicode_test)
+
+/* Constants c1-c4 and d1-d4 encode the same letters */
+
+/* Six characters translating to one utf-8 byte each. */
+static const u16 c1[] = {0x55, 0x2d, 0x42, 0x6f, 0x6f, 0x74, 0x00};
+/* One character translating to two utf-8 bytes */
+static const u16 c2[] = {0x6b, 0x61, 0x66, 0x62, 0xe1, 0x74, 0x75, 0x72, 0x00};
+/* Three characters translating to three utf-8 bytes each */
+static const u16 c3[] = {0x6f5c, 0x6c34, 0x8266, 0x00};
+/* Three letters translating to four utf-8 bytes each */
+static const u16 c4[] = {0xd801, 0xdc8d, 0xd801, 0xdc96, 0xd801, 0xdc87,
+ 0x0000};
+
+/* Illegal utf-16 strings */
+static const u16 i1[] = {0x69, 0x31, 0xdc87, 0x6c, 0x00};
+static const u16 i2[] = {0x69, 0x32, 0xd801, 0xd801, 0x6c, 0x00};
+static const u16 i3[] = {0x69, 0x33, 0xd801, 0x00};
+
+/* Six characters translating to one utf-16 word each. */
+static const char d1[] = {0x55, 0x2d, 0x42, 0x6f, 0x6f, 0x74, 0x00};
+/* Eight characters translating to one utf-16 word each */
+static const char d2[] = {0x6b, 0x61, 0x66, 0x62, 0xc3, 0xa1, 0x74, 0x75,
+ 0x72, 0x00};
+/* Three characters translating to one utf-16 word each */
+static const char d3[] = {0xe6, 0xbd, 0x9c, 0xe6, 0xb0, 0xb4, 0xe8, 0x89,
+ 0xa6, 0x00};
+/* Three letters translating to two utf-16 word each */
+static const char d4[] = {0xf0, 0x90, 0x92, 0x8d, 0xf0, 0x90, 0x92, 0x96,
+ 0xf0, 0x90, 0x92, 0x87, 0x00};
+
+/* Illegal utf-8 strings */
+static const char j1[] = {0x6a, 0x31, 0xa1, 0x6c, 0x00};
+static const char j2[] = {0x6a, 0x32, 0xc3, 0xc3, 0x6c, 0x00};
+static const char j3[] = {0x6a, 0x33, 0xf0, 0x90, 0xf0, 0x00};
+
+/* U-Boot uses UTF-16 strings in the EFI context only. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+static int ut_string16(struct unit_test_state *uts)
+{
+ char buf[20];
+
+ /* Test length and precision */
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%8.6ls", c2);
+ ut_asserteq(' ', buf[1]);
+ ut_assert(!strncmp(&buf[2], d2, 7));
+ ut_assert(!buf[9]);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%8.6ls", c4);
+ ut_asserteq(' ', buf[4]);
+ ut_assert(!strncmp(&buf[5], d4, 12));
+ ut_assert(!buf[17]);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%-8.2ls", c4);
+ ut_asserteq(' ', buf[8]);
+ ut_assert(!strncmp(buf, d4, 8));
+ ut_assert(!buf[14]);
+
+ /* Test handling of illegal utf-16 sequences */
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%ls", i1);
+ ut_asserteq_str("i1?l", buf);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%ls", i2);
+ ut_asserteq_str("i2?l", buf);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%ls", i3);
+ ut_asserteq_str("i3?", buf);
+
+ return 0;
+}
+UNICODE_TEST(ut_string16);
+#endif
+
+static int ut_utf8_get(struct unit_test_state *uts)
+{
+ const char *s;
+ s32 code;
+ int i;
+
+ /* Check characters less than 0x800 */
+ s = d2;
+ for (i = 0; i < 8; ++i) {
+ code = utf8_get((const char **)&s);
+ /* c2 is the utf-8 encoding of d2 */
+ ut_asserteq(c2[i], code);
+ if (!code)
+ break;
+ }
+ ut_asserteq_ptr(s, d2 + 9)
+
+ /* Check characters less than 0x10000 */
+ s = d3;
+ for (i = 0; i < 4; ++i) {
+ code = utf8_get((const char **)&s);
+ /* c3 is the utf-8 encoding of d3 */
+ ut_asserteq(c3[i], code);
+ if (!code)
+ break;
+ }
+ ut_asserteq_ptr(s, d3 + 9)
+
+ /* Check character greater 0xffff */
+ s = d4;
+ code = utf8_get((const char **)&s);
+ ut_asserteq(0x0001048d, code);
+ ut_asserteq_ptr(s, d4 + 4);
+
+ return 0;
+}
+UNICODE_TEST(ut_utf8_get);
+
+static int ut_utf8_put(struct unit_test_state *uts)
+{
+ char buffer[8] = { 0, };
+ char *pos;
+
+ /* Commercial at, translates to one character */
+ pos = buffer;
+ ut_assert(!utf8_put('@', &pos))
+ ut_asserteq(1, pos - buffer);
+ ut_asserteq('@', buffer[0]);
+ ut_assert(!buffer[1]);
+
+ /* Latin letter G with acute, translates to two charactes */
+ pos = buffer;
+ ut_assert(!utf8_put(0x1f4, &pos));
+ ut_asserteq(2, pos - buffer);
+ ut_asserteq_str("\xc7\xb4", buffer);
+
+ /* Tagalog letter i, translates to three characters */
+ pos = buffer;
+ ut_assert(!utf8_put(0x1701, &pos));
+ ut_asserteq(3, pos - buffer);
+ ut_asserteq_str("\xe1\x9c\x81", buffer);
+
+ /* Hamster face, translates to four characters */
+ pos = buffer;
+ ut_assert(!utf8_put(0x1f439, &pos));
+ ut_asserteq(4, pos - buffer);
+ ut_asserteq_str("\xf0\x9f\x90\xb9", buffer);
+
+ /* Illegal code */
+ pos = buffer;
+ ut_asserteq(-1, utf8_put(0xd888, &pos));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf8_put);
+
+static int ut_utf8_utf16_strlen(struct unit_test_state *uts)
+{
+ ut_asserteq(6, utf8_utf16_strlen(d1));
+ ut_asserteq(8, utf8_utf16_strlen(d2));
+ ut_asserteq(3, utf8_utf16_strlen(d3));
+ ut_asserteq(6, utf8_utf16_strlen(d4));
+
+ /* illegal utf-8 sequences */
+ ut_asserteq(4, utf8_utf16_strlen(j1));
+ ut_asserteq(4, utf8_utf16_strlen(j2));
+ ut_asserteq(3, utf8_utf16_strlen(j3));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf8_utf16_strlen);
+
+static int ut_utf8_utf16_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(3, utf8_utf16_strnlen(d1, 3));
+ ut_asserteq(6, utf8_utf16_strnlen(d1, 13));
+ ut_asserteq(6, utf8_utf16_strnlen(d2, 6));
+ ut_asserteq(2, utf8_utf16_strnlen(d3, 2));
+ ut_asserteq(4, utf8_utf16_strnlen(d4, 2));
+ ut_asserteq(6, utf8_utf16_strnlen(d4, 3));
+
+ /* illegal utf-8 sequences */
+ ut_asserteq(4, utf8_utf16_strnlen(j1, 16));
+ ut_asserteq(4, utf8_utf16_strnlen(j2, 16));
+ ut_asserteq(3, utf8_utf16_strnlen(j3, 16));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf8_utf16_strnlen);
+
+/**
+ * ut_u16_strcmp() - Compare to u16 strings.
+ *
+ * @a1: first string
+ * @a2: second string
+ * @count: number of u16 to compare
+ * Return: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
+ */
+static int ut_u16_strcmp(const u16 *a1, const u16 *a2, size_t count)
+{
+ for (; (*a1 || *a2) && count; ++a1, ++a2, --count) {
+ if (*a1 < *a2)
+ return -1;
+ if (*a1 > *a2)
+ return 1;
+ }
+ return 0;
+}
+
+static int ut_utf8_utf16_strcpy(struct unit_test_state *uts)
+{
+ u16 buf[16];
+ u16 *pos;
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d1);
+ ut_asserteq(6, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, c1, SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d2);
+ ut_asserteq(8, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, c2, SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d3);
+ ut_asserteq(3, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, c3, SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d4);
+ ut_asserteq(6, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, c4, SIZE_MAX));
+
+ /* Illegal utf-8 strings */
+ pos = buf;
+ utf8_utf16_strcpy(&pos, j1);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, L"j1?l", SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, j2);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, j3);
+ ut_asserteq(3, pos - buf);
+ ut_assert(!ut_u16_strcmp(buf, L"j3?", SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf8_utf16_strcpy);
+
+int ut_utf8_utf16_strncpy(struct unit_test_state *uts)
+{
+ u16 buf[16];
+ u16 *pos;
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d1, 4);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!buf[4]);
+ ut_assert(!ut_u16_strcmp(buf, c1, 4));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d2, 10);
+ ut_asserteq(8, pos - buf);
+ ut_assert(buf[4]);
+ ut_assert(!ut_u16_strcmp(buf, c2, SIZE_MAX));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d3, 2);
+ ut_asserteq(2, pos - buf);
+ ut_assert(!buf[2]);
+ ut_assert(!ut_u16_strcmp(buf, c3, 2));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d4, 2);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!buf[4]);
+ ut_assert(!ut_u16_strcmp(buf, c4, 4));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d4, 10);
+ ut_asserteq(6, pos - buf);
+ ut_assert(buf[5]);
+ ut_assert(!ut_u16_strcmp(buf, c4, SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf8_utf16_strncpy);
+
+static int ut_utf16_get(struct unit_test_state *uts)
+{
+ const u16 *s;
+ s32 code;
+ int i;
+
+ /* Check characters less than 0x10000 */
+ s = c2;
+ for (i = 0; i < 9; ++i) {
+ code = utf16_get((const u16 **)&s);
+ ut_asserteq(c2[i], code);
+ if (!code)
+ break;
+ }
+ ut_asserteq_ptr(c2 + 8, s);
+
+ /* Check character greater 0xffff */
+ s = c4;
+ code = utf16_get((const u16 **)&s);
+ ut_asserteq(0x0001048d, code);
+ ut_asserteq_ptr(c4 + 2, s);
+
+ return 0;
+}
+UNICODE_TEST(ut_utf16_get);
+
+static int ut_utf16_put(struct unit_test_state *uts)
+{
+ u16 buffer[4] = { 0, };
+ u16 *pos;
+
+ /* Commercial at, translates to one word */
+ pos = buffer;
+ ut_assert(!utf16_put('@', &pos));
+ ut_asserteq(1, pos - buffer);
+ ut_asserteq((u16)'@', buffer[0]);
+ ut_assert(!buffer[1]);
+
+ /* Hamster face, translates to two words */
+ pos = buffer;
+ ut_assert(!utf16_put(0x1f439, &pos));
+ ut_asserteq(2, pos - buffer);
+ ut_asserteq((u16)0xd83d, buffer[0]);
+ ut_asserteq((u16)0xdc39, buffer[1]);
+ ut_assert(!buffer[2]);
+
+ /* Illegal code */
+ pos = buffer;
+ ut_asserteq(-1, utf16_put(0xd888, &pos));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf16_put);
+
+int ut_utf16_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(3, utf16_strnlen(c1, 3));
+ ut_asserteq(6, utf16_strnlen(c1, 13));
+ ut_asserteq(6, utf16_strnlen(c2, 6));
+ ut_asserteq(2, utf16_strnlen(c3, 2));
+ ut_asserteq(2, utf16_strnlen(c4, 2));
+ ut_asserteq(3, utf16_strnlen(c4, 3));
+
+ /* illegal utf-16 word sequences */
+ ut_asserteq(4, utf16_strnlen(i1, 16));
+ ut_asserteq(4, utf16_strnlen(i2, 16));
+ ut_asserteq(3, utf16_strnlen(i3, 16));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf16_strnlen);
+
+int ut_utf16_utf8_strlen(struct unit_test_state *uts)
+{
+ ut_asserteq(6, utf16_utf8_strlen(c1));
+ ut_asserteq(9, utf16_utf8_strlen(c2));
+ ut_asserteq(9, utf16_utf8_strlen(c3));
+ ut_asserteq(12, utf16_utf8_strlen(c4));
+
+ /* illegal utf-16 word sequences */
+ ut_asserteq(4, utf16_utf8_strlen(i1));
+ ut_asserteq(4, utf16_utf8_strlen(i2));
+ ut_asserteq(3, utf16_utf8_strlen(i3));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf16_utf8_strlen);
+
+int ut_utf16_utf8_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(3, utf16_utf8_strnlen(c1, 3));
+ ut_asserteq(6, utf16_utf8_strnlen(c1, 13));
+ ut_asserteq(7, utf16_utf8_strnlen(c2, 6));
+ ut_asserteq(6, utf16_utf8_strnlen(c3, 2));
+ ut_asserteq(8, utf16_utf8_strnlen(c4, 2));
+ ut_asserteq(12, utf16_utf8_strnlen(c4, 3));
+ return 0;
+}
+UNICODE_TEST(ut_utf16_utf8_strnlen);
+
+int ut_utf16_utf8_strcpy(struct unit_test_state *uts)
+{
+ char buf[16];
+ char *pos;
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c1);
+ ut_asserteq(6, pos - buf);
+ ut_asserteq_str(d1, buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c2);
+ ut_asserteq(9, pos - buf);
+ ut_asserteq_str(d2, buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c3);
+ ut_asserteq(9, pos - buf);
+ ut_asserteq_str(d3, buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c4);
+ ut_asserteq(12, pos - buf);
+ ut_asserteq_str(d4, buf);
+
+ /* Illegal utf-16 strings */
+ pos = buf;
+ utf16_utf8_strcpy(&pos, i1);
+ ut_asserteq(4, pos - buf);
+ ut_asserteq_str("i1?l", buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, i2);
+ ut_asserteq(4, pos - buf);
+ ut_asserteq_str("i2?l", buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, i3);
+ ut_asserteq(3, pos - buf);
+ ut_asserteq_str("i3?", buf);
+
+ return 0;
+}
+UNICODE_TEST(ut_utf16_utf8_strcpy);
+
+int ut_utf16_utf8_strncpy(struct unit_test_state *uts)
+{
+ char buf[16];
+ char *pos;
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c1, 4);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!buf[4]);
+ ut_assert(!strncmp(buf, d1, 4));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c2, 10);
+ ut_asserteq(9, pos - buf);
+ ut_assert(buf[4]);
+ ut_assert(!strncmp(buf, d2, SIZE_MAX));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c3, 2);
+ ut_asserteq(6, pos - buf);
+ ut_assert(!buf[6]);
+ ut_assert(!strncmp(buf, d3, 6));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c4, 2);
+ ut_asserteq(8, pos - buf);
+ ut_assert(!buf[8]);
+ ut_assert(!strncmp(buf, d4, 8));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c4, 10);
+ ut_asserteq(12, pos - buf);
+ ut_assert(buf[5]);
+ ut_assert(!strncmp(buf, d4, SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(ut_utf16_utf8_strncpy);
+
+int ut_utf_to_lower(struct unit_test_state *uts)
+{
+ ut_asserteq('@', utf_to_lower('@'));
+ ut_asserteq('a', utf_to_lower('A'));
+ ut_asserteq('z', utf_to_lower('Z'));
+ ut_asserteq('[', utf_to_lower('['));
+ ut_asserteq('m', utf_to_lower('m'));
+ /* Latin letter O with diaresis (umlaut) */
+ ut_asserteq(0x00f6, utf_to_lower(0x00d6));
+#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION
+ /* Cyrillic letter I*/
+ ut_asserteq(0x0438, utf_to_lower(0x0418));
+#endif
+ return 0;
+}
+UNICODE_TEST(ut_utf_to_lower);
+
+int ut_utf_to_upper(struct unit_test_state *uts)
+{
+ ut_asserteq('`', utf_to_upper('`'));
+ ut_asserteq('A', utf_to_upper('a'));
+ ut_asserteq('Z', utf_to_upper('z'));
+ ut_asserteq('{', utf_to_upper('{'));
+ ut_asserteq('M', utf_to_upper('M'));
+ /* Latin letter O with diaresis (umlaut) */
+ ut_asserteq(0x00d6, utf_to_upper(0x00f6));
+#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION
+ /* Cyrillic letter I */
+ ut_asserteq(0x0418, utf_to_upper(0x0438));
+#endif
+ return 0;
+}
+UNICODE_TEST(ut_utf_to_upper);
+
+int do_ut_unicode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct unit_test *tests = ll_entry_start(struct unit_test, unicode_test);
+ const int n_ents = ll_entry_count(struct unit_test, unicode_test);
+
+ return cmd_ut_category("Unicode", tests, n_ents, argc, argv);
+}