mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-09 01:05:36 +00:00
cpu: Support a target CPU having a variable page size
Support target CPUs having a page size which isn't knownn at compile time. To use this, the CPU implementation should: * define TARGET_PAGE_BITS_VARY * not define TARGET_PAGE_BITS * define TARGET_PAGE_BITS_MIN to the smallest value it might possibly want for TARGET_PAGE_BITS * call set_preferred_target_page_bits() in its realize function to indicate the actual preferred target page size for the CPU (and report any error from it) In CONFIG_USER_ONLY, the CPU implementation should continue to define TARGET_PAGE_BITS appropriately for the guest OS page size. Machines which want to take advantage of having the page size something larger than TARGET_PAGE_BITS_MIN must set the MachineClass minimum_page_bits field to a value which they guarantee will be no greater than the preferred page size for any CPU they create. Note that changing the target page size by setting minimum_page_bits is a migration compatibility break for that machine. For debugging purposes, attempts to use TARGET_PAGE_SIZE before it has been finally confirmed will assert. Backports commit 20bccb82ff3ea09bcb7c4ee226d3160cab15f7da from qemu
This commit is contained in:
parent
a7229cc08a
commit
db8b0a82b1
|
@ -184,6 +184,9 @@ struct uc_struct {
|
||||||
RAMList ram_list;
|
RAMList ram_list;
|
||||||
// Renamed from "alloc_hint" in qemu.
|
// Renamed from "alloc_hint" in qemu.
|
||||||
unsigned phys_map_node_alloc_hint;
|
unsigned phys_map_node_alloc_hint;
|
||||||
|
// Used when a target's page bits can vary
|
||||||
|
int target_page_bits;
|
||||||
|
bool target_page_bits_decided;
|
||||||
|
|
||||||
// qemu/cpu-exec.c
|
// qemu/cpu-exec.c
|
||||||
BounceBuffer bounce;
|
BounceBuffer bounce;
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_aarch64
|
#define set_float_rounding_mode set_float_rounding_mode_aarch64
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_aarch64
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_aarch64
|
||||||
#define set_flush_to_zero set_flush_to_zero_aarch64
|
#define set_flush_to_zero set_flush_to_zero_aarch64
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_aarch64
|
||||||
#define set_swi_errno set_swi_errno_aarch64
|
#define set_swi_errno set_swi_errno_aarch64
|
||||||
#define sextract32 sextract32_aarch64
|
#define sextract32 sextract32_aarch64
|
||||||
#define sextract64 sextract64_aarch64
|
#define sextract64 sextract64_aarch64
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_aarch64eb
|
#define set_float_rounding_mode set_float_rounding_mode_aarch64eb
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_aarch64eb
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_aarch64eb
|
||||||
#define set_flush_to_zero set_flush_to_zero_aarch64eb
|
#define set_flush_to_zero set_flush_to_zero_aarch64eb
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_aarch64eb
|
||||||
#define set_swi_errno set_swi_errno_aarch64eb
|
#define set_swi_errno set_swi_errno_aarch64eb
|
||||||
#define sextract32 sextract32_aarch64eb
|
#define sextract32 sextract32_aarch64eb
|
||||||
#define sextract64 sextract64_aarch64eb
|
#define sextract64 sextract64_aarch64eb
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_arm
|
#define set_float_rounding_mode set_float_rounding_mode_arm
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_arm
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_arm
|
||||||
#define set_flush_to_zero set_flush_to_zero_arm
|
#define set_flush_to_zero set_flush_to_zero_arm
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_arm
|
||||||
#define set_swi_errno set_swi_errno_arm
|
#define set_swi_errno set_swi_errno_arm
|
||||||
#define sextract32 sextract32_arm
|
#define sextract32 sextract32_arm
|
||||||
#define sextract64 sextract64_arm
|
#define sextract64 sextract64_arm
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_armeb
|
#define set_float_rounding_mode set_float_rounding_mode_armeb
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_armeb
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_armeb
|
||||||
#define set_flush_to_zero set_flush_to_zero_armeb
|
#define set_flush_to_zero set_flush_to_zero_armeb
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_armeb
|
||||||
#define set_swi_errno set_swi_errno_armeb
|
#define set_swi_errno set_swi_errno_armeb
|
||||||
#define sextract32 sextract32_armeb
|
#define sextract32 sextract32_armeb
|
||||||
#define sextract64 sextract64_armeb
|
#define sextract64 sextract64_armeb
|
||||||
|
|
37
qemu/exec.c
37
qemu/exec.c
|
@ -68,11 +68,40 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool set_preferred_target_page_bits(struct uc_struct *uc, int bits)
|
||||||
|
{
|
||||||
|
/* The target page size is the lowest common denominator for all
|
||||||
|
* the CPUs in the system, so we can only make it smaller, never
|
||||||
|
* larger. And we can't make it smaller once we've committed to
|
||||||
|
* a particular size.
|
||||||
|
*/
|
||||||
|
#ifdef TARGET_PAGE_BITS_VARY
|
||||||
|
assert(bits >= TARGET_PAGE_BITS_MIN);
|
||||||
|
if (uc->target_page_bits == 0 || uc->target_page_bits > bits) {
|
||||||
|
if (uc->target_page_bits_decided) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uc->target_page_bits = bits;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
/* current CPU in the current thread. It is only valid inside
|
/* current CPU in the current thread. It is only valid inside
|
||||||
cpu_exec() */
|
cpu_exec() */
|
||||||
//DEFINE_TLS(CPUState *, current_cpu);
|
//DEFINE_TLS(CPUState *, current_cpu);
|
||||||
|
|
||||||
|
static void finalize_target_page_bits(struct uc_struct *uc)
|
||||||
|
{
|
||||||
|
#ifdef TARGET_PAGE_BITS_VARY
|
||||||
|
if (uc->target_page_bits == 0) {
|
||||||
|
uc->target_page_bits = TARGET_PAGE_BITS_MIN;
|
||||||
|
}
|
||||||
|
uc->target_page_bits_decided = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct PhysPageEntry PhysPageEntry;
|
typedef struct PhysPageEntry PhysPageEntry;
|
||||||
|
|
||||||
struct PhysPageEntry {
|
struct PhysPageEntry {
|
||||||
|
@ -1826,6 +1855,14 @@ static void memory_map_init(struct uc_struct *uc)
|
||||||
|
|
||||||
void cpu_exec_init_all(struct uc_struct *uc)
|
void cpu_exec_init_all(struct uc_struct *uc)
|
||||||
{
|
{
|
||||||
|
/* The data structures we set up here depend on knowing the page size,
|
||||||
|
* so no more changes can be made after this point.
|
||||||
|
* In an ideal world, nothing we did before we had finished the
|
||||||
|
* machine setup would care about the target page size, and we could
|
||||||
|
* do this much later, rather than requiring board models to state
|
||||||
|
* up front what their requirements are.
|
||||||
|
*/
|
||||||
|
finalize_target_page_bits(uc);
|
||||||
io_mem_init(uc);
|
io_mem_init(uc);
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
memory_map_init(uc);
|
memory_map_init(uc);
|
||||||
|
|
|
@ -2591,6 +2591,7 @@ symbols = (
|
||||||
'set_float_rounding_mode',
|
'set_float_rounding_mode',
|
||||||
'set_flush_inputs_to_zero',
|
'set_flush_inputs_to_zero',
|
||||||
'set_flush_to_zero',
|
'set_flush_to_zero',
|
||||||
|
'set_preferred_target_page_bits',
|
||||||
'set_swi_errno',
|
'set_swi_errno',
|
||||||
'sextract32',
|
'sextract32',
|
||||||
'sextract64',
|
'sextract64',
|
||||||
|
|
|
@ -230,6 +230,13 @@ void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val,
|
||||||
|
|
||||||
/* page related stuff */
|
/* page related stuff */
|
||||||
|
|
||||||
|
#ifdef TARGET_PAGE_BITS_VARY
|
||||||
|
#define TARGET_PAGE_BITS ({ assert(target_page_bits_decided); \
|
||||||
|
target_page_bits; })
|
||||||
|
#else
|
||||||
|
#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
|
#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
|
||||||
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
||||||
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct QEMUMachine {
|
||||||
int max_cpus;
|
int max_cpus;
|
||||||
int is_default;
|
int is_default;
|
||||||
int arch;
|
int arch;
|
||||||
|
int minimum_page_bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner,
|
void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner,
|
||||||
|
@ -46,6 +47,12 @@ MachineClass *find_default_machine(struct uc_struct *uc, int arch);
|
||||||
/**
|
/**
|
||||||
* MachineClass:
|
* MachineClass:
|
||||||
* @qemu_machine: #QEMUMachine
|
* @qemu_machine: #QEMUMachine
|
||||||
|
* @minimum_page_bits:
|
||||||
|
* If non-zero, the board promises never to create a CPU with a page size
|
||||||
|
* smaller than this, so QEMU can use a more efficient larger page
|
||||||
|
* size than the target architecture's minimum. (Attempting to create
|
||||||
|
* such a CPU will fail.) Note that changing this is a migration
|
||||||
|
* compatibility break for the machine.
|
||||||
*/
|
*/
|
||||||
struct MachineClass {
|
struct MachineClass {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
|
@ -61,6 +68,7 @@ struct MachineClass {
|
||||||
int max_cpus;
|
int max_cpus;
|
||||||
int is_default;
|
int is_default;
|
||||||
int arch;
|
int arch;
|
||||||
|
int minimum_page_bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -66,6 +66,18 @@ bool tcg_enabled(struct uc_struct *uc);
|
||||||
struct uc_struct;
|
struct uc_struct;
|
||||||
void cpu_exec_init_all(struct uc_struct *uc);
|
void cpu_exec_init_all(struct uc_struct *uc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set_preferred_target_page_bits:
|
||||||
|
* @bits: number of bits needed to represent an address within the page
|
||||||
|
*
|
||||||
|
* Set the preferred target page size (the actual target page
|
||||||
|
* size may be smaller than any given CPU's preference).
|
||||||
|
* Returns true on success, false on failure (which can only happen
|
||||||
|
* if this is called after the system has already finalized its
|
||||||
|
* choice of page size and the requested page size is smaller than that).
|
||||||
|
*/
|
||||||
|
bool set_preferred_target_page_bits(struct uc_struct *uc, int bits);
|
||||||
|
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
|
|
||||||
// support for calling functions before main code is executed.
|
// support for calling functions before main code is executed.
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_m68k
|
#define set_float_rounding_mode set_float_rounding_mode_m68k
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_m68k
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_m68k
|
||||||
#define set_flush_to_zero set_flush_to_zero_m68k
|
#define set_flush_to_zero set_flush_to_zero_m68k
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_m68k
|
||||||
#define set_swi_errno set_swi_errno_m68k
|
#define set_swi_errno set_swi_errno_m68k
|
||||||
#define sextract32 sextract32_m68k
|
#define sextract32 sextract32_m68k
|
||||||
#define sextract64 sextract64_m68k
|
#define sextract64 sextract64_m68k
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_mips
|
#define set_float_rounding_mode set_float_rounding_mode_mips
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mips
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mips
|
||||||
#define set_flush_to_zero set_flush_to_zero_mips
|
#define set_flush_to_zero set_flush_to_zero_mips
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_mips
|
||||||
#define set_swi_errno set_swi_errno_mips
|
#define set_swi_errno set_swi_errno_mips
|
||||||
#define sextract32 sextract32_mips
|
#define sextract32 sextract32_mips
|
||||||
#define sextract64 sextract64_mips
|
#define sextract64 sextract64_mips
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_mips64
|
#define set_float_rounding_mode set_float_rounding_mode_mips64
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mips64
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mips64
|
||||||
#define set_flush_to_zero set_flush_to_zero_mips64
|
#define set_flush_to_zero set_flush_to_zero_mips64
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_mips64
|
||||||
#define set_swi_errno set_swi_errno_mips64
|
#define set_swi_errno set_swi_errno_mips64
|
||||||
#define sextract32 sextract32_mips64
|
#define sextract32 sextract32_mips64
|
||||||
#define sextract64 sextract64_mips64
|
#define sextract64 sextract64_mips64
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_mips64el
|
#define set_float_rounding_mode set_float_rounding_mode_mips64el
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mips64el
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mips64el
|
||||||
#define set_flush_to_zero set_flush_to_zero_mips64el
|
#define set_flush_to_zero set_flush_to_zero_mips64el
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_mips64el
|
||||||
#define set_swi_errno set_swi_errno_mips64el
|
#define set_swi_errno set_swi_errno_mips64el
|
||||||
#define sextract32 sextract32_mips64el
|
#define sextract32 sextract32_mips64el
|
||||||
#define sextract64 sextract64_mips64el
|
#define sextract64 sextract64_mips64el
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_mipsel
|
#define set_float_rounding_mode set_float_rounding_mode_mipsel
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mipsel
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_mipsel
|
||||||
#define set_flush_to_zero set_flush_to_zero_mipsel
|
#define set_flush_to_zero set_flush_to_zero_mipsel
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_mipsel
|
||||||
#define set_swi_errno set_swi_errno_mipsel
|
#define set_swi_errno set_swi_errno_mipsel
|
||||||
#define sextract32 sextract32_mipsel
|
#define sextract32 sextract32_mipsel
|
||||||
#define sextract64 sextract64_mipsel
|
#define sextract64 sextract64_mipsel
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_powerpc
|
#define set_float_rounding_mode set_float_rounding_mode_powerpc
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_powerpc
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_powerpc
|
||||||
#define set_flush_to_zero set_flush_to_zero_powerpc
|
#define set_flush_to_zero set_flush_to_zero_powerpc
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_powerpc
|
||||||
#define set_swi_errno set_swi_errno_powerpc
|
#define set_swi_errno set_swi_errno_powerpc
|
||||||
#define sextract32 sextract32_powerpc
|
#define sextract32 sextract32_powerpc
|
||||||
#define sextract64 sextract64_powerpc
|
#define sextract64 sextract64_powerpc
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_sparc
|
#define set_float_rounding_mode set_float_rounding_mode_sparc
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_sparc
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_sparc
|
||||||
#define set_flush_to_zero set_flush_to_zero_sparc
|
#define set_flush_to_zero set_flush_to_zero_sparc
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_sparc
|
||||||
#define set_swi_errno set_swi_errno_sparc
|
#define set_swi_errno set_swi_errno_sparc
|
||||||
#define sextract32 sextract32_sparc
|
#define sextract32 sextract32_sparc
|
||||||
#define sextract64 sextract64_sparc
|
#define sextract64 sextract64_sparc
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_sparc64
|
#define set_float_rounding_mode set_float_rounding_mode_sparc64
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_sparc64
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_sparc64
|
||||||
#define set_flush_to_zero set_flush_to_zero_sparc64
|
#define set_flush_to_zero set_flush_to_zero_sparc64
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_sparc64
|
||||||
#define set_swi_errno set_swi_errno_sparc64
|
#define set_swi_errno set_swi_errno_sparc64
|
||||||
#define sextract32 sextract32_sparc64
|
#define sextract32 sextract32_sparc64
|
||||||
#define sextract64 sextract64_sparc64
|
#define sextract64 sextract64_sparc64
|
||||||
|
|
15
qemu/vl.c
15
qemu/vl.c
|
@ -136,6 +136,20 @@ int machine_initialize(struct uc_struct *uc)
|
||||||
OBJECT_CLASS(machine_class))));
|
OBJECT_CLASS(machine_class))));
|
||||||
uc->machine_state = current_machine;
|
uc->machine_state = current_machine;
|
||||||
current_machine->uc = uc;
|
current_machine->uc = uc;
|
||||||
|
|
||||||
|
// Unicorn: FIXME: this should be uncommented
|
||||||
|
// However due to the "stellar" way unicorn
|
||||||
|
// handles multiple targets (e.g. the YOLO
|
||||||
|
// Python script named header_gen.py), this
|
||||||
|
// results in a compilation error.
|
||||||
|
//if (machine_class->minimum_page_bits) {
|
||||||
|
// if (!set_preferred_target_page_bits(uc, machine_class->minimum_page_bits)) {
|
||||||
|
// /* This would be a board error: specifying a minimum smaller than
|
||||||
|
// * a target's compile-time fixed setting.
|
||||||
|
// */
|
||||||
|
// g_assert_not_reached();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
uc->cpu_exec_init_all(uc);
|
uc->cpu_exec_init_all(uc);
|
||||||
|
|
||||||
machine_class->max_cpus = 1;
|
machine_class->max_cpus = 1;
|
||||||
|
@ -168,6 +182,7 @@ static void machine_class_init(struct uc_struct *uc, ObjectClass *oc, void *data
|
||||||
mc->max_cpus = qm->max_cpus;
|
mc->max_cpus = qm->max_cpus;
|
||||||
mc->is_default = qm->is_default;
|
mc->is_default = qm->is_default;
|
||||||
mc->arch = qm->arch;
|
mc->arch = qm->arch;
|
||||||
|
mc->minimum_page_bits = qm->minimum_page_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_register_machine(struct uc_struct *uc, QEMUMachine *m, const char *type_machine,
|
void qemu_register_machine(struct uc_struct *uc, QEMUMachine *m, const char *type_machine,
|
||||||
|
|
|
@ -2585,6 +2585,7 @@
|
||||||
#define set_float_rounding_mode set_float_rounding_mode_x86_64
|
#define set_float_rounding_mode set_float_rounding_mode_x86_64
|
||||||
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_x86_64
|
#define set_flush_inputs_to_zero set_flush_inputs_to_zero_x86_64
|
||||||
#define set_flush_to_zero set_flush_to_zero_x86_64
|
#define set_flush_to_zero set_flush_to_zero_x86_64
|
||||||
|
#define set_preferred_target_page_bits set_preferred_target_page_bits_x86_64
|
||||||
#define set_swi_errno set_swi_errno_x86_64
|
#define set_swi_errno set_swi_errno_x86_64
|
||||||
#define sextract32 sextract32_x86_64
|
#define sextract32 sextract32_x86_64
|
||||||
#define sextract64 sextract64_x86_64
|
#define sextract64 sextract64_x86_64
|
||||||
|
|
Loading…
Reference in a new issue