diff --git a/install_zip/Android.mk b/install_zip/Android.mk
index 925e112..33f58f5 100644
--- a/install_zip/Android.mk
+++ b/install_zip/Android.mk
@@ -40,9 +40,11 @@ $(MULTIROM_ZIP_TARGET): multirom trampoline fw_mounter signapk bbootimg mrom_kex
 	cp -a $(TARGET_OUT_OPTIONAL_EXECUTABLES)/mrom_adbd $(MULTIROM_INST_DIR)/multirom/adbd
 
 	if $(MR_ENCRYPTION); then \
-		mkdir -p $(MULTIROM_INST_DIR)/multirom/enc; \
+		mkdir -p $(MULTIROM_INST_DIR)/multirom/enc/res; \
 		cp -a $(TARGET_ROOT_OUT)/trampoline_encmnt $(MULTIROM_INST_DIR)/multirom/enc/; \
 		cp -a $(TARGET_OUT_EXECUTABLES)/linker $(MULTIROM_INST_DIR)/multirom/enc/; \
+		cp -a $(install_zip_path)/prebuilt-installer/multirom/res/Roboto-Regular.ttf $(MULTIROM_INST_DIR)/multirom/enc/res/; \
+		\
 		cp -a $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfslollipop.so $(MULTIROM_INST_DIR)/multirom/enc/; \
 		cp -a $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so $(MULTIROM_INST_DIR)/multirom/enc/; \
 		cp -a $(TARGET_OUT_SHARED_LIBRARIES)/libc.so $(MULTIROM_INST_DIR)/multirom/enc/; \
diff --git a/lib/framebuffer.h b/lib/framebuffer.h
index 72bef7f..3e74ea0 100644
--- a/lib/framebuffer.h
+++ b/lib/framebuffer.h
@@ -255,6 +255,7 @@ fb_img *fb_text_finalize(fb_text_proto *p);
 void fb_text_set_color(fb_img *img, uint32_t color);
 void fb_text_set_size(fb_img *img, int size);
 void fb_text_set_content(fb_img *img, const char *text);
+char *fb_text_get_content(fb_img *img);
 
 void fb_text_drop_cache_unused(void);
 void fb_text_destroy(fb_img *i);
diff --git a/lib/framebuffer_truetype.c b/lib/framebuffer_truetype.c
index a3f32d5..2fbe174 100644
--- a/lib/framebuffer_truetype.c
+++ b/lib/framebuffer_truetype.c
@@ -134,7 +134,7 @@ static int convert_ft_bitmap(FT_BitmapGlyph bit, px_type color, px_type *res_dat
     return 0;
 }
 
-static struct glyphs_entry *get_cache_for_size(const int style, const int size)
+static struct glyphs_entry *get_cache_for_size(int style, const int size)
 {
     int error;
     struct glyphs_entry *res;
@@ -152,6 +152,7 @@ static struct glyphs_entry *get_cache_for_size(const int style, const int size)
     if(!cache.glyphs[style])
         cache.glyphs[style] = imap_create();
 
+retry_load:
     res = imap_get_val(cache.glyphs[style], size);
     if(!res)
     {
@@ -161,8 +162,16 @@ static struct glyphs_entry *get_cache_for_size(const int style, const int size)
         error = FT_New_Face(cache.ft_lib, buff, 0, &res->face);
         if(error)
         {
-            ERROR("font load failed with %d\n", error);
+            ERROR("font style %d load failed with %d\n", style, error);
             free(res);
+
+            if(style != STYLE_NORMAL)
+            {
+                ERROR("Retrying with STYLE_NORMAL instead.");
+                style = STYLE_NORMAL;
+                goto retry_load;
+            }
+
             return NULL;
         }
 
@@ -690,6 +699,12 @@ void fb_text_set_content(fb_img *img, const char *text)
     fb_items_unlock();
 }
 
+char *fb_text_get_content(fb_img *img)
+{
+    text_extra *ex = img->extra;
+    return ex->text;
+}
+
 inline void center_text(fb_img *text, int targetX, int targetY, int targetW, int targetH)
 {
     text_extra *ex = text->extra;
diff --git a/lib/keyboard.c b/lib/keyboard.c
index da6e347..cb2eda1 100644
--- a/lib/keyboard.c
+++ b/lib/keyboard.c
@@ -25,38 +25,37 @@
 #define KS(x) ((x-1) << 16)
 #define GET_KS(x) ((x & 0xFF0000)>> 16)
 
-#define KEY_EMPTY 0xFF
-#define KEY_EMPTY_HALF 0xFE
-#define KEY_ENTER 0xFD
-#define KEY_CLEAR 0xFC
-#define KEY_SHIFT 0xFB
+#define KF(x) ((x) << 8)
+#define GET_KF(x) ((x & 0xFF00) >> 8)
+#define KFLAG_HALF KF(0x01)
 
 static const char *specialKeys[] = {
-    NULL,  // KEY_EMPTY
-    NULL,  // KEY_EMPTY_HALF
-    "OK",  // KEY_ENTER
-    "X",   // KEY_CLEAR
+    NULL,  // OSK_EMPTY
+    "OK",  // OSK_ENTER
+    "<",   // OSK_BACKSPACE
+    "^",  // OSK_SHIFT
+    "X",   // OSK_CLEAR
 };
 
 // One keycode
 // bits | 0         | 8       | 16      |
 // data | character | flags   | colspan |
 static const uint32_t pinKeycodeMap[] = {
-    '1', '2', '3', KEY_EMPTY,
-    '4', '5', '6', KEY_EMPTY,
-    '7', '8', '9', KEY_CLEAR,
-    '0' | KS(3),   KEY_ENTER,
+    OSK_EMPTY | KFLAG_HALF, '1', '2', '3', OSK_EMPTY,
+    OSK_EMPTY | KFLAG_HALF, '4', '5', '6', OSK_CLEAR,
+    OSK_EMPTY | KFLAG_HALF, '7', '8', '9', OSK_BACKSPACE,
+    OSK_EMPTY | KFLAG_HALF, '0' | KS(3),   OSK_ENTER,
     0
 };
 
 // rows, cols
-static const uint32_t pinKeycodeMapDimensions[] = { 4, 4 };
+static const uint32_t pinKeycodeMapDimensions[] = { 4, 5 };
 
 static const uint32_t normalKeycodeMap[] = {
     'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
-    KEY_EMPTY_HALF, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
-    KEY_SHIFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', KEY_CLEAR | KS(2),
-    KEY_CLEAR, ' ' | KS(6), '.', KEY_ENTER | KS(2),
+    OSK_EMPTY| KFLAG_HALF, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
+    OSK_SHIFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', OSK_BACKSPACE | KS(2),
+    OSK_BACKSPACE, ' ' | KS(6), '.', OSK_ENTER | KS(2),
     0
 };
 
@@ -73,7 +72,9 @@ struct keyboard_btn_data {
 static void keyboard_btn_clicked(void *data)
 {
     struct keyboard_btn_data *d = data;
-    INFO("keyboard %c pressed.", (char)(d->k->keycode_map[d->btn_idx] & 0xFF));
+    uint8_t keycode = (d->k->keycode_map[d->btn_idx] & 0xFF);
+    if(d->k->key_pressed)
+        d->k->key_pressed(d->k->key_pressed_data, keycode);
 }
 
 static int keyboard_init_map(struct keyboard *k, const uint32_t *map, const uint32_t *dimen)
@@ -95,9 +96,10 @@ static int keyboard_init_map(struct keyboard *k, const uint32_t *map, const uint
         code = (map[i] & 0xFF);
         w = (GET_KS(map[i])+1)*btn_w + PADDING*GET_KS(map[i]);
 
-        if(code == KEY_EMPTY_HALF)
+        if(map[i] & KFLAG_HALF)
             w /= 2;
-        else if(code != KEY_EMPTY)
+
+        if(code != OSK_EMPTY)
         {
             btn = mzalloc(sizeof(button));
             btn->x = x;
@@ -112,7 +114,7 @@ static int keyboard_init_map(struct keyboard *k, const uint32_t *map, const uint
             btn->clicked = keyboard_btn_clicked;
 
             buf[0] = (map[i] & 0xFF);
-            button_init_ui(btn, buf[0] >= 0 ? buf : specialKeys[0xFF - (map[i] & 0xFF)], SIZE_NORMAL);
+            button_init_ui(btn, ((int8_t)buf[0]) >= 0 ? buf : specialKeys[0xFF - (map[i] & 0xFF)], SIZE_NORMAL);
             list_add(&k->btns, btn);
         }
 
@@ -131,7 +133,7 @@ static int keyboard_init_map(struct keyboard *k, const uint32_t *map, const uint
     return 0;
 }
 
-struct keyboard *keyboard_create(int x, int y, int w, int h)
+struct keyboard *keyboard_create(int type, int x, int y, int w, int h)
 {
     struct keyboard *k = mzalloc(sizeof(struct keyboard));
     k->x = x;
@@ -139,13 +141,28 @@ struct keyboard *keyboard_create(int x, int y, int w, int h)
     k->w = w;
     k->h = h;
 
-    //keyboard_init_map(k, pinKeycodeMap, pinKeycodeMapDimensions);
-    keyboard_init_map(k, normalKeycodeMap, normalKeycodeMapDimensions);
+    switch(type)
+    {
+        case KEYBOARD_PIN:
+            keyboard_init_map(k, pinKeycodeMap, pinKeycodeMapDimensions);
+            break;
+        case KEYBOARD_NORMAL:
+        default:
+            keyboard_init_map(k, normalKeycodeMap, normalKeycodeMapDimensions);
+            break;
+    }
 
     return k;
 }
 
 void keyboard_destroy(struct keyboard *k)
 {
-
+    list_clear(&k->btns, &button_destroy);
+    free(k);
+}
+
+void keyboard_set_callback(struct keyboard *k, keyboard_on_pressed_callback callback, void *data)
+{
+    k->key_pressed = callback;
+    k->key_pressed_data = data;
 }
diff --git a/lib/keyboard.h b/lib/keyboard.h
index 541f8d1..174eee0 100644
--- a/lib/keyboard.h
+++ b/lib/keyboard.h
@@ -23,14 +23,27 @@
 #include "framebuffer.h"
 #include "button.h"
 
+#define OSK_EMPTY 0xFF
+#define OSK_ENTER 0xFE
+#define OSK_BACKSPACE 0xFD
+#define OSK_SHIFT 0xFC
+#define OSK_CLEAR 0xFB
+
+typedef void (*keyboard_on_pressed_callback)(void *data, uint8_t keycode);
 struct keyboard
 {
     FB_ITEM_POS
     button **btns;
     const uint32_t *keycode_map;
+    keyboard_on_pressed_callback key_pressed;
+    void *key_pressed_data;
 };
 
-struct keyboard *keyboard_create(int x, int y, int w, int h);
+#define KEYBOARD_PIN 0
+#define KEYBOARD_NORMAL 1
+
+struct keyboard *keyboard_create(int type, int x, int y, int w, int h);
+void keyboard_set_callback(struct keyboard *k, keyboard_on_pressed_callback callback, void *data);
 void keyboard_destroy(struct keyboard *k);
 
 #endif
diff --git a/multirom_ui.c b/multirom_ui.c
index 23a3076..9bd00ec 100644
--- a/multirom_ui.c
+++ b/multirom_ui.c
@@ -490,7 +490,7 @@ void multirom_ui_refresh_usb_handler(void)
     pthread_mutex_unlock(&exit_code_mutex);
 }
 
-void multirom_ui_start_pong(int action)
+void multirom_ui_start_pong(void *data)
 {
     pthread_mutex_lock(&exit_code_mutex);
     loop_act |= LOOP_START_PONG;
diff --git a/multirom_ui.h b/multirom_ui.h
index 9126cad..b9593dd 100644
--- a/multirom_ui.h
+++ b/multirom_ui.h
@@ -50,7 +50,7 @@ void multirom_ui_switch_btn(void *data);
 void multirom_ui_fill_rom_list(listview *view, int mask);
 void multirom_ui_auto_boot(void);
 void multirom_ui_refresh_usb_handler(void);
-void multirom_ui_start_pong(int action);
+void multirom_ui_start_pong(void *data);
 void multirom_ui_init_theme(int tab);
 void multirom_ui_destroy_theme(void);
 
diff --git a/trampoline/encryption.c b/trampoline/encryption.c
index 63c2f34..43646a1 100644
--- a/trampoline/encryption.c
+++ b/trampoline/encryption.c
@@ -23,6 +23,8 @@
 #include "../lib/fstab.h"
 #include "../lib/util.h"
 #include "../lib/log.h"
+#include "encryption.h"
+#include "../trampoline_encmnt/encmnt_defines.h"
 
 static char encmnt_cmd_arg[64] = { 0 };
 static char *const encmnt_cmd[] = { "/mrom_enc/trampoline_encmnt", encmnt_cmd_arg, NULL };
@@ -32,7 +34,7 @@ int encryption_before_mount(struct fstab *fstab)
 {
     int exit_code = -1;
     char *output = NULL, *itr;
-    int res = -1;
+    int res = ENC_RES_ERR;
 
     mkdir_recursive("/system/bin", 0755);
     remove("/system/bin/linker");
@@ -54,6 +56,13 @@ int encryption_before_mount(struct fstab *fstab)
     while(itr >= output && isspace(*itr))
         *itr-- = 0;
 
+    if(strcmp(output, ENCMNT_BOOT_INTERNAL_OUTPUT) == 0)
+    {
+        INFO("trampoline_encmnt requested to boot internal ROM.");
+        res = ENC_RES_BOOT_INTERNAL;
+        goto exit;
+    }
+
     if(!strstartswith(output, "/dev"))
     {
         ERROR("Invalid trampoline_encmnt output: %s", output);
@@ -71,7 +80,7 @@ int encryption_before_mount(struct fstab *fstab)
     fstab_update_device(fstab, datap->device, output);
     fstab_dump(fstab);
 
-    res = 0;
+    res = ENC_RES_OK;
 exit:
     free(output);
     return res;
diff --git a/trampoline/encryption.h b/trampoline/encryption.h
index d25604a..ffc4934 100644
--- a/trampoline/encryption.h
+++ b/trampoline/encryption.h
@@ -18,6 +18,10 @@
 #ifndef ENCRYPTION_H
 #define ENCRYPTION_H
 
+#define ENC_RES_ERR -1
+#define ENC_RES_OK 0
+#define ENC_RES_BOOT_INTERNAL 1
+
 #ifdef MR_ENCRYPTION
 int encryption_before_mount(struct fstab *fstab);
 int encryption_destroy(void);
diff --git a/trampoline/trampoline.c b/trampoline/trampoline.c
index dd44203..f5f3c4d 100644
--- a/trampoline/trampoline.c
+++ b/trampoline/trampoline.c
@@ -125,8 +125,11 @@ static void mount_and_run(struct fstab *fstab)
 
     mkdir(REALDATA, 0755);
 
-    if(encryption_before_mount(fstab) < 0)
+    int enc_res = encryption_before_mount(fstab);
+    if(enc_res == ENC_RES_ERR)
         ERROR("Decryption failed, trying to mount anyway - might be unencrypted device.");
+    else if(enc_res == ENC_RES_BOOT_INTERNAL)
+        return;
 
     int mount_err = -1;
     struct fstab_part *p_itr = p;
@@ -183,7 +186,7 @@ static void mount_and_run(struct fstab *fstab)
         return;
     }
 
-    //adb_init(path_multirom);
+    adb_init(path_multirom);
     run_multirom();
     adb_quit();
 
@@ -332,7 +335,7 @@ int main(int argc, char *argv[])
 #endif
 
     // REMOVE, DEBUGGING!
-    adb_init("/adb_sbin/");
+    //adb_init("/adb_sbin/");
 
     // mount and run multirom from sdcard
     mount_and_run(fstab);
diff --git a/trampoline_encmnt/Android.mk b/trampoline_encmnt/Android.mk
index 14d814c..89c7ce3 100644
--- a/trampoline_encmnt/Android.mk
+++ b/trampoline_encmnt/Android.mk
@@ -16,4 +16,6 @@ LOCAL_SRC_FILES := \
     encmnt.c \
     pw_ui.c \
 
+include $(multirom_local_path)/device_defines.mk
+
 include $(BUILD_EXECUTABLE)
diff --git a/trampoline_encmnt/encmnt.c b/trampoline_encmnt/encmnt.c
index 56d24a9..8d67192 100644
--- a/trampoline_encmnt/encmnt.c
+++ b/trampoline_encmnt/encmnt.c
@@ -28,6 +28,7 @@
 #include "crypto/lollipop/cryptfs.h"
 
 #include "pw_ui.h"
+#include "encmnt_defines.h"
 
 #define CMD_NONE 0
 #define CMD_DECRYPT 1
@@ -121,10 +122,22 @@ static int handle_decrypt(int stdout_fd, const char *password)
             return -1;
         }
     }
-    else if(pw_ui_run(pwtype) < 0)
+    else
     {
-        ERROR("pw_ui_get() failed!");
-        return -1;
+        switch(pw_ui_run(pwtype))
+        {
+            default:
+            case ENCMNT_UIRES_ERROR:
+                ERROR("pw_ui_run() failed!");
+                return -1;
+            case ENCMNT_UIRES_BOOT_INTERNAL:
+                INFO("Wants to boot internal!");
+                write(stdout_fd, ENCMNT_BOOT_INTERNAL_OUTPUT, strlen(ENCMNT_BOOT_INTERNAL_OUTPUT));
+                fsync(stdout_fd);
+                return 0;
+            case ENCMNT_UIRES_PASS_OK:
+                break;
+        }
     }
 
     d = opendir("/dev/block/");
@@ -179,7 +192,7 @@ int main(int argc, char *argv[])
     klog_set_level(6);
 
     mrom_set_log_tag("trampoline_encmnt");
-    mrom_set_dir("/adb_sbin/");
+    mrom_set_dir("/mrom_enc/");
 
     for(i = 1; i < argc; ++i)
     {
diff --git a/trampoline_encmnt/encmnt_defines.h b/trampoline_encmnt/encmnt_defines.h
new file mode 100644
index 0000000..311f1f3
--- /dev/null
+++ b/trampoline_encmnt/encmnt_defines.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of MultiROM.
+ *
+ * MultiROM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MultiROM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MultiROM.  If not, see .
+ */
+
+#ifndef ENCMNT_DEFINES_H
+#define ENCMNT_DEFINES_H
+
+#define ENCMNT_BOOT_INTERNAL_OUTPUT "boot-internal-requested"
+
+#define ENCMNT_UIRES_BOOT_INTERNAL 1
+#define ENCMNT_UIRES_PASS_OK 0
+#define ENCMNT_UIRES_ERROR -1
+
+#endif
diff --git a/trampoline_encmnt/pw_ui.c b/trampoline_encmnt/pw_ui.c
index c344f92..87fbd38 100644
--- a/trampoline_encmnt/pw_ui.c
+++ b/trampoline_encmnt/pw_ui.c
@@ -16,12 +16,202 @@
  */
 
 #include 
+#include 
+#include 
 
 #include "pw_ui.h"
+#include "encmnt_defines.h"
 #include "../lib/framebuffer.h"
 #include "../lib/colors.h"
 #include "../lib/log.h"
 #include "../lib/input.h"
+#include "../lib/keyboard.h"
+#include "../lib/util.h"
+#include "../lib/notification_card.h"
+#include "../lib/animation.h"
+#include "../lib/workers.h"
+
+#include "crypto/lollipop/cryptfs.h"
+
+#define HEADER_HEIGHT (110*DPI_MUL)
+
+struct pwui_type_pass_data {
+    fb_text *passwd_text;
+    fb_rect *cursor_rect;
+    struct keyboard *keyboard;
+};
+
+static pthread_mutex_t exit_code_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int exit_code = ENCMNT_UIRES_ERROR;
+static void *pwui_type_data = NULL;
+static fb_text *invalid_pass_text = NULL;
+static button *boot_primary_btn = NULL;
+
+static void boot_internal_clicked(void *data)
+{
+    ncard_builder *b = ncard_create_builder();
+    ncard_set_pos(b, NCARD_POS_CENTER);
+    ncard_set_text(b, "Booting the primary ROM...");
+    ncard_show(b, 1);
+
+    pthread_mutex_lock(&exit_code_mutex);
+    exit_code = ENCMNT_UIRES_BOOT_INTERNAL;
+    pthread_mutex_unlock(&exit_code_mutex);
+}
+
+static void fade_rect_alpha_step(void *data, float interpolated)
+{
+    fb_rect *r = data;
+    r->color = (((int)(0xFF*interpolated)) << 24);
+    fb_request_draw();
+}
+
+static void reveal_rect_alpha_step(void *data, float interpolated)
+{
+    fb_rect *r = data;
+    interpolated = 1.f - interpolated;
+    r->color = (r->color & ~(0xFF << 24)) | (((int)(0xFF*interpolated)) << 24);
+    fb_request_draw();
+}
+
+static void try_password(const char *pass)
+{
+    fb_text_set_content(invalid_pass_text, "");
+
+    ncard_builder *b = ncard_create_builder();
+    ncard_set_pos(b, NCARD_POS_CENTER);
+    ncard_set_text(b, "Verifying password...");
+    ncard_show(b, 1);
+
+    if(cryptfs_check_passwd(pass) != 0)
+    {
+        ncard_hide();
+        fb_text_set_content(invalid_pass_text, "Invalid password!");
+        center_text(invalid_pass_text, 0, -1, fb_width, -1);
+    }
+    else
+    {
+        ncard_builder *b = ncard_create_builder();
+        ncard_set_pos(b, NCARD_POS_CENTER);
+        ncard_set_text(b, "Correct!");
+        ncard_show(b, 1);
+
+        fb_rect *r = fb_add_rect_lvl(10000, 0, 0, fb_width, fb_height, 0x00000000);
+        call_anim *a = call_anim_create(r, fade_rect_alpha_step, 500, INTERPOLATOR_ACCELERATE);
+        call_anim_add(a);
+
+        pthread_mutex_lock(&exit_code_mutex);
+        exit_code = ENCMNT_UIRES_PASS_OK;
+        pthread_mutex_unlock(&exit_code_mutex);
+    }
+}
+
+static void type_pass_key_pressed(void *data, uint8_t code)
+{
+    struct pwui_type_pass_data *d = data;
+
+    if(code < 128)
+    {
+        char *old = fb_text_get_content(d->passwd_text);
+        char *new_text = malloc(strlen(old)+2);
+        sprintf(new_text, "%s%c", old, (char)code);
+        fb_text_set_content(d->passwd_text, new_text);
+        center_text(d->passwd_text, 0, 0, fb_width, fb_height);
+        free(new_text);
+        return;
+    }
+
+    switch(code)
+    {
+        case OSK_BACKSPACE:
+        {
+            char *old = fb_text_get_content(d->passwd_text);
+            int len = strlen(old);
+            if(len <= 0)
+                break;
+
+            char *new_text = strdup(old);
+            new_text[len-1] = 0;
+            fb_text_set_content(d->passwd_text, new_text);
+            center_text(d->passwd_text, 0, 0, fb_width, fb_height);
+            free(new_text);
+            break;
+        }
+        case OSK_CLEAR:
+            fb_text_set_content(d->passwd_text, "");
+            break;
+        case OSK_ENTER:
+            try_password(fb_text_get_content(d->passwd_text));
+            break;
+    }
+}
+
+static void type_pass_init(int pwtype)
+{
+    struct pwui_type_pass_data *d = mzalloc(sizeof(struct pwui_type_pass_data));
+    d->keyboard = keyboard_create(pwtype == CRYPT_TYPE_PIN ? KEYBOARD_PIN : KEYBOARD_NORMAL,
+            0, fb_height*0.65, fb_width, fb_height*0.35);
+    keyboard_set_callback(d->keyboard, type_pass_key_pressed, d);
+
+    d->passwd_text = fb_add_text(0, 0, C_TEXT, SIZE_BIG, "");
+    center_text(d->passwd_text, 0, 0, fb_width, fb_height);
+
+    pwui_type_data = d;
+}
+
+static void type_pass_destroy(int pwtype)
+{
+    struct pwui_type_pass_data *d = pwui_type_data;
+    keyboard_destroy(d->keyboard);
+    free(d);
+    pwui_type_data = NULL;
+}
+
+static void init_ui(int pwtype)
+{
+    fb_add_rect_lvl(100, 0, 0, fb_width, HEADER_HEIGHT, C_HIGHLIGHT_BG);
+
+    fb_text_proto *p = fb_text_create(0, 0, C_HIGHLIGHT_TEXT, SIZE_EXTRA, "Encrypted device");
+    p->level = 110;
+    fb_text *t = fb_text_finalize(p);
+    center_text(t, -1, 0, -1, HEADER_HEIGHT);
+    t->x = t->y;
+
+    t = fb_add_text(0, HEADER_HEIGHT + 200*DPI_MUL, C_TEXT, SIZE_NORMAL, "Please enter your password:");
+    center_text(t, 0, -1, fb_width, -1);
+
+    invalid_pass_text = fb_add_text(0, 0, 0xFFFF0000, SIZE_BIG, "");
+    center_text(invalid_pass_text, -1, HEADER_HEIGHT, -1, 200*DPI_MUL);
+
+    boot_primary_btn = mzalloc(sizeof(button));
+    boot_primary_btn->w = fb_width*0.30;
+    boot_primary_btn->h = HEADER_HEIGHT;
+    boot_primary_btn->x = fb_width - boot_primary_btn->w;
+    boot_primary_btn->y = 0;
+    boot_primary_btn->level_off = 101;
+    boot_primary_btn->clicked = &boot_internal_clicked;
+    button_init_ui(boot_primary_btn, "BOOT PRIMARY ROM", SIZE_SMALL);
+
+    switch(pwtype)
+    {
+        case CRYPT_TYPE_PASSWORD:
+        case CRYPT_TYPE_PIN:
+            type_pass_init(pwtype);
+            break;
+    }
+}
+
+static void destroy_ui(int pwtype)
+{
+    switch(pwtype)
+    {
+        case CRYPT_TYPE_PASSWORD:
+        case CRYPT_TYPE_PIN:
+            type_pass_destroy(pwtype);
+            break;
+    }
+    button_destroy(boot_primary_btn);
+}
 
 int pw_ui_run(int pwtype)
 {
@@ -34,17 +224,42 @@ int pw_ui_run(int pwtype)
     fb_freeze(1);
     fb_set_background(C_BACKGROUND);
 
+    workers_start();
+    anim_init(1.f);
+
+    init_ui(pwtype);
+
     start_input_thread();
 
     fb_freeze(0);
-    fb_request_draw();
+
+    fb_rect *r = fb_add_rect_lvl(1000, 0, 0, fb_width, fb_height, BLACK);
+    call_anim *a = call_anim_create(r, reveal_rect_alpha_step, 500, INTERPOLATOR_ACCELERATE);
+    a->on_finished_call = fb_remove_item;
+    a->on_finished_data = r;
+    call_anim_add(a);
 
     while(1) {
-        sleep(1);
+        pthread_mutex_lock(&exit_code_mutex);
+        const int c = exit_code;
+        pthread_mutex_unlock(&exit_code_mutex);
+
+        if(c != ENCMNT_UIRES_ERROR)
+            break;
+
+        usleep(100000);
     }
 
-    stop_input_thread();
+    anim_stop(1);
+    fb_freeze(1);
+    fb_force_draw();
 
+    stop_input_thread();
+    workers_stop();
+
+    destroy_ui(pwtype);
+
+    fb_clear();
     fb_close();
-    return -1;
+    return exit_code;
 }