Merge "aboot: fwlock: Add support for recovery PIN"
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index 35373d8..c03d456 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -1724,7 +1724,7 @@
 #ifdef MDTP_SUPPORT
 	/* Go through Firmware Lock verification before continue with boot process */
 	mdtp_fwlock_verify_lock();
-	fbcon_clear();
+	display_image_on_screen();
 #endif /* MDTP_SUPPORT */
 
 #if VERIFIED_BOOT
@@ -2384,7 +2384,7 @@
 #ifdef MDTP_SUPPORT
 	/* Go through Firmware Lock verification before continue with boot process */
 	mdtp_fwlock_verify_lock();
-	fbcon_clear();
+	display_image_on_screen();
 #endif /* MDTP_SUPPORT */
 
 	if (target_is_emmc_boot())
@@ -2868,7 +2868,7 @@
 #ifdef MDTP_SUPPORT
 			/* Go through Firmware Lock verification before continue with boot process */
 			mdtp_fwlock_verify_lock();
-			fbcon_clear();
+			display_image_on_screen();
 #endif /* MDTP_SUPPORT */
 
 			boot_linux_from_mmc();
diff --git a/app/aboot/mdtp.c b/app/aboot/mdtp.c
index 60b97b0..9ae9890 100644
--- a/app/aboot/mdtp.c
+++ b/app/aboot/mdtp.c
@@ -76,7 +76,7 @@
 		return -1;
 	}
 
-	dprintf(INFO, "mdtp: read_DIP: SUCCESS, read %d bytes\n", actual_partition_size);
+	dprintf(SPEW, "mdtp: read_DIP: SUCCESS, read %d bytes\n", actual_partition_size);
 
 	return 0;
 }
@@ -85,7 +85,6 @@
 static int write_DIP(DIP_t *dip)
 {
 	unsigned long long ptn = 0;
-	uint32_t partition_size;
 	uint32_t block_size = mmc_get_device_blocksize();
 
 	int index = INVALID_PTN;
@@ -94,30 +93,25 @@
 
 	index = partition_get_index("dip");
 	ptn = partition_get_offset(index);
+
 	if(ptn == 0)
 	{
 		return -1;
 	}
 
-	partition_size = partition_get_size(index);
-
-	if(partition_size < size)
-	{
-		dprintf(CRITICAL, "mdtp: write_DIP: ERROR, DIP partition too small\n");
-		return -1;
-	}
-
-	if(mmc_write(ptn, ROUNDUP(size, block_size), (void *)dip))
+	if(mmc_write(ptn, ROUNDUP(sizeof(DIP_t), block_size), (void *)dip))
 	{
 		dprintf(CRITICAL, "mdtp: write_DIP: ERROR, cannot read DIP info\n");
 		return -1;
 	}
 
+	dprintf(SPEW, "mdtp: write_DIP: SUCCESS, write %d bytes\n", ROUNDUP(sizeof(DIP_t), block_size));
+
 	return 0;
 }
 
-/* Provision the DIP by storing the default DIP into the EMMC */
-static void provision_DIP()
+/* Deactivate MDTP by storing the default DIP into the EMMC */
+static void write_deactivated_DIP()
 {
 	DIP_t *enc_dip;
 	DIP_t *dec_dip;
@@ -126,14 +120,14 @@
 	enc_dip = malloc(sizeof(DIP_t));
 	if (enc_dip == NULL)
 	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot allocate DIP\n");
+		dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot allocate DIP\n");
 		return;
 	}
 
 	dec_dip = malloc(sizeof(DIP_t));
 	if (dec_dip == NULL)
 	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot allocate DIP\n");
+		dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot allocate DIP\n");
 		free(enc_dip);
 		return;
 	}
@@ -145,21 +139,14 @@
 	ret = mdtp_tzbsp_enc_hash_DIP(dec_dip, enc_dip);
 	if(ret < 0)
 	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot cipher DIP\n");
+		dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot cipher DIP\n");
 		goto out;
 	}
 
 	ret = write_DIP(enc_dip);
 	if(ret < 0)
 	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot write DIP\n");
-		goto out;
-	}
-
-	ret = mdtp_tzbsp_set_provisioned_fuse();
-	if(ret < 0)
-	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot set DIP_PROVISIONED fuse\n\n");
+		dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot write DIP\n");
 		goto out;
 	}
 
@@ -178,7 +165,7 @@
 	uint32_t block_size = mmc_get_device_blocksize();
 	uint32_t actual_partition_size = ROUNDUP(size, block_size);
 
-	dprintf(INFO, "mdtp: verify_partition_single_hash: %s, %u\n", name, size);
+	dprintf(SPEW, "mdtp: verify_partition_single_hash: %s, %u\n", name, size);
 
 	ASSERT(name != NULL);
 	ASSERT(hash_table != NULL);
@@ -208,7 +195,7 @@
 		return -1;
 	}
 
-	dprintf(INFO, "verify_partition_single_hash: %s: VERIFIED!\n", name);
+	dprintf(SPEW, "verify_partition_single_hash: %s: VERIFIED!\n", name);
 
 	return 0;
 }
@@ -228,7 +215,7 @@
 	uint32_t bytes_to_read;
 	uint32_t block_num = 0;
 
-	dprintf(INFO, "mdtp: verify_partition_block_hash: %s, %u\n", name, size);
+	dprintf(SPEW, "mdtp: verify_partition_block_hash: %s, %u\n", name, size);
 
 	ASSERT(name != NULL);
 	ASSERT(hash_table != NULL);
@@ -287,7 +274,7 @@
 		force_verify_block += 1;
 	}
 
-	dprintf(INFO, "verify_partition_block_hash: %s: VERIFIED!\n", name);
+	dprintf(SPEW, "verify_partition_block_hash: %s: VERIFIED!\n", name);
 
 	return 0;
 }
@@ -321,6 +308,75 @@
 	return 0;
 }
 
+/* Display the recovery UI to allow the user to enter the PIN and continue boot */
+static int display_recovery_ui(DIP_t *dip)
+{
+	uint32_t pin_length = 0;
+	char entered_pin[MDTP_MAX_PIN_LEN+1] = {0};
+	uint32_t i;
+	uint32_t equal_count = 0, different_count = 0;
+
+	if (dip->mdtp_cfg.enable_local_pin_authentication)
+	{
+		dprintf(SPEW, "mdtp: display_recovery_ui: Local deactivation enabled\n");
+
+		pin_length = strlen(dip->mdtp_cfg.mdtp_pin.mdtp_pin);
+
+		if (pin_length > MDTP_MAX_PIN_LEN || pin_length < MDTP_MIN_PIN_LEN)
+		{
+			dprintf(CRITICAL, "mdtp: display_recovery_ui: Error, invalid PIN length\n");
+			display_error_msg();
+			return -1;
+		}
+
+		// Set entered_pin to initial '0' string + null terminator
+		for (i=0; i<pin_length; i++)
+		{
+			entered_pin[i] = '0';
+		}
+
+		// Allow the user to enter the PIN as many times as he wishes
+		// (with INVALID_PIN_DELAY_MSECONDS after each failed attempt)
+		while (1)
+		{
+		    get_pin_from_user(entered_pin, pin_length);
+
+		    // Go over the entire PIN in any case, to prevent side-channel attacks
+		    for (i=0; i<pin_length; i++)
+		    {
+		        if (dip->mdtp_cfg.mdtp_pin.mdtp_pin[i] == entered_pin[i])
+		            equal_count++;
+		        else
+		            different_count++;
+		    }
+
+		    if (equal_count == pin_length)
+		    {
+		        // Valid PIN - deactivate and continue boot
+		        dprintf(SPEW, "mdtp: display_recovery_ui: valid PIN, continue boot\n");
+		        write_deactivated_DIP();
+		        return 0;
+		    }
+		    else
+		    {
+		        // Invalid PIN - display an appropriate message (which also includes a wait
+		        // for INVALID_PIN_DELAY_MSECONDS), and allow the user to try again
+		        dprintf(CRITICAL, "mdtp: display_recovery_ui: ERROR, invalid PIN\n");
+		        display_invalid_pin_msg();
+
+		        equal_count = 0;
+		        different_count = 0;
+		    }
+		}
+	}
+	else
+	{
+		dprintf(CRITICAL, "mdtp: display_recovery_ui: Local deactivation disabled, unable to display recovery UI\n");
+		display_error_msg();
+		return -1;
+	}
+}
+
 /* Verify all protected partitinons according to the DIP */
 static int verify_all_partitions(DIP_t *dip, verify_result_t *verify_result)
 {
@@ -340,8 +396,6 @@
 	}
 	else if (dip->status == DIP_STATUS_ACTIVATED)
 	{
-		show_checking_msg();
-
 		for(i=0; i<MAX_PARTITIONS; i++)
 		{
 			if(dip->partition_cfg[i].lock_enabled && dip->partition_cfg[i].size)
@@ -361,14 +415,12 @@
 		if (verify_failure)
 		{
 			dprintf(CRITICAL, "mdtp: verify_all_partitions: Failed partition verification\n");
-			show_invalid_msg();
-			return -1;
+			return 0;
 		}
 
 	}
 
 	*verify_result = VERIFY_OK;
-	show_OK_msg();
 	return 0;
 }
 
@@ -385,14 +437,14 @@
 	enc_dip = malloc(ROUNDUP(sizeof(DIP_t), block_size));
 	if (enc_dip == NULL)
 	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot allocate DIP\n");
+		dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot allocate DIP\n");
 		return;
 	}
 
 	dec_dip = malloc(ROUNDUP(sizeof(DIP_t), block_size));
 	if (dec_dip == NULL)
 	{
-		dprintf(CRITICAL, "mdtp: provision_DIP: ERROR, cannot allocate DIP\n");
+		dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot allocate DIP\n");
 		free(enc_dip);
 		return;
 	}
@@ -410,7 +462,7 @@
 	if(ret < 0)
 	{
 		dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot verify DIP\n");
-		show_invalid_msg();
+		display_error_msg();
 		goto out;
 	}
 
@@ -418,7 +470,7 @@
 	if(!verified)
 	{
 		dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, corrupted DIP\n");
-		show_invalid_msg();
+		display_error_msg();
 		goto out;
 	}
 
@@ -432,14 +484,15 @@
 
 	if (verify_result == VERIFY_OK)
 	{
-		dprintf(INFO, "mdtp: validate_DIP_and_firmware: Verify OK\n");
+		dprintf(SPEW, "mdtp: validate_DIP_and_firmware: Verify OK\n");
 	}
 	else if (verify_result  == VERIFY_FAILED)
 	{
 		dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, corrupted firmware\n");
+		display_recovery_ui(dec_dip);
 	} else /* VERIFY_SKIPPED */
 	{
-		dprintf(INFO, "mdtp: validate_DIP_and_firmware: Verify skipped\n");
+		dprintf(SPEW, "mdtp: validate_DIP_and_firmware: Verify skipped\n");
 	}
 
 out:
diff --git a/app/aboot/mdtp.h b/app/aboot/mdtp.h
index 9975899..d40807a 100644
--- a/app/aboot/mdtp.h
+++ b/app/aboot/mdtp.h
@@ -34,9 +34,13 @@
 #define MAX_PARTITIONS 3
 #define MAX_PARTITION_NAME_LEN 100
 #define HASH_LEN 32
+#define MDTP_MIN_PIN_LEN 5
 #define MDTP_MAX_PIN_LEN 8
 #define DIP_PADDING 11
 
+#define INITIAL_DELAY_MSECONDS      5000
+#define INVALID_PIN_DELAY_MSECONDS  5000
+
 #define ROUND_TO_PAGE(x,y) (((x) + (y)) & (~(y)))
 #define MDTP_FWLOCK_BLOCK_SIZE (1024*1024*16)
 #define MDTP_FWLOCK_MAX_FILES (100)
@@ -104,19 +108,59 @@
 	VERIFY_FAILED,
 } verify_result_t;
 
-/* Start Firmware Lock verification process */
+
+/**
+ * mdtp_fwlock_verify_lock
+ *
+ * Start Firmware Lock verification process.
+ *
+ * @return - negative value for an error, 0 for success.
+ */
 int mdtp_fwlock_verify_lock();
 
-/* Return whether the MDTP is currently enabled or disabled in HW */
+/**
+ * mdtp_fuse_get_enabled
+ *
+ * Return whether the MDTP is currently enabled or
+ * disabled in HW.
+ *
+ * @param[out] enabled: set to true if MDTP enabled,
+ * false otherwise.
+ *
+ * @return - negative value for an error, 0 for success.
+ */
 int mdtp_fuse_get_enabled(bool *enabled);
 
-/* Display the "Firmware Valid" screen */
-void show_OK_msg();
+/**
+ * get_pin_from_user
+ *
+ * Display the recovery PIN screen and set received buffer
+ * with the PIN the user has entered.
+ *
+ * @param[out] entered_pin: buffer holding the received PIN.
+ * @param[in]  pin_length:  PIN length (and also entered_pin buffer length).
+ *
+ * @return - None.
+ */
+void get_pin_from_user(char *entered_pin, uint32_t pin_length);
 
-/* Display the "Firmware Invalid" screen */
-void show_invalid_msg();
+/**
+ * display_invalid_pin_msg
+ *
+ * User has entered invalid PIN, display error message and
+ * allow the user to try again.
+ *
+ * @return - None.
+ */
+void display_invalid_pin_msg();
 
-/* Display the "Verifying Firmware" screen */
-void show_checking_msg();
+/**
+ * display_error_msg
+ *
+ * Display error message and stop boot process.
+ *
+ * @return - None.
+ */
+void display_error_msg();
 
 #endif
diff --git a/app/aboot/mdtp_ui.c b/app/aboot/mdtp_ui.c
index 30d70d6..48d899e 100644
--- a/app/aboot/mdtp_ui.c
+++ b/app/aboot/mdtp_ui.c
@@ -32,31 +32,88 @@
 #include <mmc.h>
 #include <partition_parser.h>
 #include <stdlib.h>
+#include <string.h>
 #include "mdtp.h"
 
-#define MDTP_IMAGE_WIDTH 500
-#define MDTP_IMAGE_HEIGHT 800
-#define MDTP_UX_DELAY 1000
-#define MDTP_OK_OFFSET 0x0
-#define MDTP_CHECKING_OFFSET 0x200000
-#define MDTP_INVALID_OFFSET 0x400000
-#define MDTP_RECOVERED_OFFSET 0x600000
+// Image dimensions
+#define MDTP_ERROR_MSG_WIDTH                (1412)
+#define MDTP_ERROR_MSG_HEIGHT               (212)
+#define MDTP_MAIN_TEXT_WIDTH                (1364)
+#define MDTP_MAIN_TEXT_HEIGHT               (288)
+#define MDTP_PIN_DIGIT_WIDTH                (180)
+#define MDTP_PIN_DIGIT_HEIGHT               (180)
+#define MDTP_OK_BUTTON_WIDTH                (644)
+#define MDTP_OK_BUTTON_HEIGHT               (158)
+#define MDTP_DIGITS_INSTRUCTIONS_WIDTH      (1384)
+#define MDTP_DIGITS_INSTRUCTIONS_HEIGHT     (166)
+#define MDTP_PIN_INSTRUCTIONS_WIDTH         (920)
+#define MDTP_PIN_INSTRUCTIONS_HEIGHT        (204)
 
-extern uint32_t target_volume_down(void);
-extern void fbcon_putImage(struct fbimage *fbimg, bool flag);
+// Image offsets
+#define MDTP_ERROR_MSG_OFFSET               (0x1000)
+#define MDTP_INITIAL_DELAY_OFFSET           (0xDD000)
+#define MDTP_ENTER_PIN_OFFSET               (0x1FD000)
+#define MDTP_INVALID_PIN_OFFSET             (0x31D000)
+#define MDTP_PIN_DIGIT_0_OFFSET             (0x43D000)
+#define MDTP_PIN_DIGITS_OFFSET              (0x18000)
+#define MDTP_PIN_SELECTED_DIGIT_0_OFFSET    (MDTP_PIN_DIGIT_0_OFFSET + 10*MDTP_PIN_DIGITS_OFFSET)  // (0x52D000)
+#define MDTP_OK_BUTTON_OFFSET               (0x61D000)
+#define MDTP_SELECTED_OK_BUTTON_OFFSET      (0x668000)
+#define MDTP_DIGITS_INSTRUCTIONS_OFFSET     (0x6B3000)
+#define MDTP_PIN_INSTRUCTIONS_OFFSET        (0x75C000)
+
+// Image releative locations
+#define ERROR_MESSAGE_RELATIVE_Y_LOCATION   (0.18)
+#define MAIN_TEXT_RELATIVE_Y_LOCATION       (0.33)
+#define PIN_RELATIVE_Y_LOCATION             (0.47)
+#define PIN_TEXT_RELATIVE_Y_LOCATION        (0.57)
+#define OK_BUTTON_RELATIVE_Y_LOCATION       (0.75)
+#define OK_TEXT_RELATIVE_Y_LOCATION         (0.82)
+
+#define DIGIT_SPACE                         (12)
+
+#define MDTP_PRESSING_DELAY_MSEC            (400)
+#define MDTP_MAX_IMAGE_SIZE                 (1183000)  //size in bytes, includes some extra bytes since we round up to block size in read
+#define RGB888_BLACK                        (0x000000)
+#define BITS_PER_BYTE                       (8)
+
+
+#define CENTER_IMAGE_ON_X_AXIS(image_width,screen_width)         ((screen_width-image_width)/2)
+
 extern void mdelay(unsigned msecs);
+extern uint32_t target_volume_up();
+extern uint32_t target_volume_down();
 
-static struct fbimage mdtp_header;/* = {0};*/
+struct mdtp_fbimage {
+    uint32_t width;
+    uint32_t height;
+    uint8_t image[MDTP_MAX_IMAGE_SIZE];
+};
 
-/********************************************************************************/
+/*----------------------------------------------------------------------------
+ * Global Variables
+ * -------------------------------------------------------------------------*/
 
-/* Load the "Firmware Valid" image from EMMC */
-static struct fbimage* mdtp_images_mmc_OK()
+static uint32_t g_pin_frames_x_location[MDTP_MAX_PIN_LEN] = {0};
+static uint32_t g_pin_frames_y_location = 0;
+
+static bool g_initial_screen_displayed = false;
+
+static struct mdtp_fbimage g_mdtp_header;
+static struct fbcon_config *fb_config = NULL;
+
+/*----------------------------------------------------------------------------
+ * Local Functions
+ * -------------------------------------------------------------------------*/
+
+/**
+ * Load images from EMMC
+ */
+static struct mdtp_fbimage* mdtp_read_mmc_image(uint32_t offset, uint32_t width, uint32_t height)
 {
 	int index = INVALID_PTN;
 	unsigned long long ptn = 0;
-	struct fbcon_config *fb_display = NULL;
-	struct fbimage *logo = &mdtp_header;
+	struct mdtp_fbimage *logo = &g_mdtp_header;
 	uint32_t block_size = mmc_get_device_blocksize();
 
 	index = partition_get_index("mdtp");
@@ -71,231 +128,475 @@
 		return NULL;
 	}
 
-	fb_display = fbcon_display();
-	if (fb_display)
+	if (fb_config)
 	{
-		uint8_t *base = (uint8_t *) fb_display->base;
-		base += LOGO_IMG_OFFSET;
+		uint8_t *base = logo->image;
 
-		if (mmc_read(ptn, (void*)base, ROUNDUP(MDTP_IMAGE_WIDTH*MDTP_IMAGE_HEIGHT*3, block_size))) {
+		if (mmc_read(ptn+offset, (void*)base, ROUNDUP(width*height*3, block_size))) {
 				fbcon_clear();
 				dprintf(CRITICAL, "ERROR: mdtp image read failed\n");
 				return NULL;
 		}
-		logo->image = base;
+		logo->width = width;
+		logo->height = height;
 	}
 
 	return logo;
 }
 
-/* Load the "Firmware Invalid" image from EMMC */
-static struct fbimage* mdtp_images_mmc_INVALID()
+/**
+ * flush fbcon display
+ *
+ * The function is duplicated from fbcon.c
+ */
+static void fbcon_flush(void)
 {
-	int index = INVALID_PTN;
-	unsigned long long ptn = 0;
-	struct fbcon_config *fb_display = NULL;
-	struct fbimage *logo = &mdtp_header;
-	uint32_t block_size = mmc_get_device_blocksize();
+	if (fb_config->update_start)
+		fb_config->update_start();
+	if (fb_config->update_done)
+		while (!fb_config->update_done());
+}
 
-	index = partition_get_index("mdtp");
-	if (index == 0) {
-		dprintf(CRITICAL, "ERROR: mdtp Partition table not found\n");
-		return NULL;
-	}
+/**
+ * Clear complete section on the screen.
+ * The section is of section_height, and is located from the y
+ * coordinate and down.
+ */
+static void fbcon_clear_section(uint32_t y, uint32_t section_height)
+{
+	unsigned image_base;
+	unsigned bytes_per_bpp;
 
-	ptn = partition_get_offset(index);
-	if (ptn == 0) {
-		dprintf(CRITICAL, "ERROR: mdtp Partition invalid\n");
-		return NULL;
-	}
-
-	fb_display = fbcon_display();
-	if (fb_display)
+	if (fb_config)
 	{
-		uint8_t *base = (uint8_t *) fb_display->base;
-		base += LOGO_IMG_OFFSET;
+	    image_base = (y *(fb_config->width));
+		bytes_per_bpp = ((fb_config->bpp) / BITS_PER_BYTE);
 
-		if (mmc_read(ptn+MDTP_INVALID_OFFSET, (void*)base, ROUNDUP(MDTP_IMAGE_WIDTH*MDTP_IMAGE_HEIGHT*3, block_size))) {
-				fbcon_clear();
-				dprintf(CRITICAL, "ERROR: mdtp image read failed\n");
-				return NULL;
-		}
-		logo->image = base;
+		unsigned count = fb_config->width*section_height;
+		memset(fb_config->base + image_base*bytes_per_bpp, RGB888_BLACK, count*bytes_per_bpp);
+
+		fbcon_flush();
 	}
-
-	return logo;
-}
-
-/* Load the "Verifying Firmware" image from EMMC */
-static struct fbimage* mdtp_images_mmc_CHECKING()
-{
-	int index = INVALID_PTN;
-	unsigned long long ptn = 0;
-	struct fbcon_config *fb_display = NULL;
-	struct fbimage *logo = &mdtp_header;
-	uint32_t block_size = mmc_get_device_blocksize();
-
-	index = partition_get_index("mdtp");
-	if (index == 0) {
-		dprintf(CRITICAL, "ERROR: mdtp Partition table not found\n");
-		return NULL;
-	}
-
-	ptn = partition_get_offset(index);
-	if (ptn == 0) {
-		dprintf(CRITICAL, "ERROR: mdtp Partition invalid\n");
-		return NULL;
-	}
-
-	fb_display = fbcon_display();
-	if (fb_display)
+	else
 	{
-		uint8_t *base = (uint8_t *) fb_display->base;
-		base += LOGO_IMG_OFFSET;
-
-		if (mmc_read(ptn+MDTP_CHECKING_OFFSET, (void*)base, ROUNDUP(MDTP_IMAGE_WIDTH*MDTP_IMAGE_HEIGHT*3, block_size))) {
-				fbcon_clear();
-				dprintf(CRITICAL, "ERROR: mdtp image read failed\n");
-				return NULL;
-		}
-		logo->image = base;
+	    dprintf(CRITICAL,"ERROR: fbcon_config struct is NULL\n");
+	    display_error_msg();
 	}
-
-	return logo;
 }
 
-/* Load the "Verifying Firmware" image from EMMC */
-static struct fbimage* mdtp_images_mmc_RECOVERED()
+/**
+ * Put image at a specific (x,y) location on the screen.
+ * Duplicated from fbcon.c, with modifications to allow (x,y) location (instead of a centered image),
+ * and display bmp images properly (order of copying the lines to the screen was reversed)
+ */
+static void fbcon_putImage_in_location(struct mdtp_fbimage *fbimg, uint32_t x, uint32_t y)
 {
-	int index = INVALID_PTN;
-	unsigned long long ptn = 0;
-	struct fbcon_config *fb_display = NULL;
-	struct fbimage *logo = &mdtp_header;
-	uint32_t block_size = mmc_get_device_blocksize();
+    unsigned i = 0;
+    unsigned bytes_per_bpp;
+    unsigned image_base;
+    unsigned width, pitch, height;
+    unsigned char *logo_base = NULL;
 
-	index = partition_get_index("mdtp");
-	if (index == 0) {
-		dprintf(CRITICAL, "ERROR: mdtp Partition table not found\n");
-		return NULL;
+    if (!fb_config) {
+		dprintf(CRITICAL,"ERROR: NULL configuration, image cannot be displayed\n");
+		return;
 	}
 
-	ptn = partition_get_offset(index);
-	if (ptn == 0) {
-		dprintf(CRITICAL, "ERROR: mdtp Partition invalid\n");
-		return NULL;
+	if(fbimg) {
+		width = pitch = fbimg->width;
+		height = fbimg->height;
+		logo_base = (unsigned char *)fbimg->image;
+	}
+	else {
+	    dprintf(CRITICAL,"ERROR: invalid image struct\n");
+	    return;
 	}
 
-	fb_display = fbcon_display();
-	if (fb_display)
+	bytes_per_bpp = ((fb_config->bpp) / BITS_PER_BYTE);
+
+#if DISPLAY_TYPE_MIPI
+	if (bytes_per_bpp == 3)
 	{
-		uint8_t *base = (uint8_t *) fb_display->base;
-		base += LOGO_IMG_OFFSET;
+		if (fbimg->width == fb_config->width && fbimg->height == fb_config->height)
+			return;
 
-		if (mmc_read(ptn+MDTP_RECOVERED_OFFSET, (void*)base, ROUNDUP(MDTP_IMAGE_WIDTH*MDTP_IMAGE_HEIGHT*3, block_size))) {
-				fbcon_clear();
-				dprintf(CRITICAL, "ERROR: mdtp image read failed\n");
-				return NULL;
+		if (fbimg->width > fb_config->width || fbimg->height > fb_config->height)
+		{
+		    dprintf(CRITICAL,"ERROR: invalid image size, larger than the screen\n");
+		    return;
 		}
-		logo->image = base;
+
+		image_base = ( (y *(fb_config->width)) + x);
+		for (i = 0; i < height; i++) {
+			memcpy (fb_config->base + ((image_base + (i * (fb_config->width))) * bytes_per_bpp),
+				logo_base + ((height - 1 - i) * pitch * bytes_per_bpp), width * bytes_per_bpp);
+		}
 	}
 
-	return logo;
+	fbcon_flush();
+
+#if DISPLAY_MIPI_PANEL_NOVATEK_BLUE
+	if(is_cmd_mode_enabled())
+        mipi_dsi_cmd_mode_trigger();
+#endif
+
+#else
+    if (bytes_per_bpp == 2)
+    {
+        for (i = 0; i < fbimg->width; i++)
+        {
+            memcpy (fb_config->base + ((image_base + (i * (fb_config->width))) * bytes_per_bpp),
+		   fbimg->image + (i * fbimg->height * bytes_per_bpp),
+		   fbimg->height * bytes_per_bpp);
+        }
+    }
+    fbcon_flush();
+#endif
 }
 
-/* Show the "Firmware Valid" image */
-static void display_image_on_screen_OK()
+/**
+ * Display main error message
+ */
+static int display_error_message()
 {
-	struct fbimage *fbimg;
+    struct mdtp_fbimage *fbimg;
 
-	fbcon_clear();
-	fbimg = mdtp_images_mmc_OK();
-	fbimg->header.width = MDTP_IMAGE_WIDTH;
-	fbimg->header.height = MDTP_IMAGE_HEIGHT;
+    fb_config = fbcon_display();
 
-	dprintf(CRITICAL, "display_image_on_screen_OK\n");
-	fbcon_putImage(fbimg, true);
+    if (fb_config)
+	{
+        uint32_t x = CENTER_IMAGE_ON_X_AXIS(MDTP_ERROR_MSG_WIDTH,fb_config->width);
+		uint32_t y = ((fb_config->height)*ERROR_MESSAGE_RELATIVE_Y_LOCATION);
+
+        fbimg = mdtp_read_mmc_image(MDTP_ERROR_MSG_OFFSET, MDTP_ERROR_MSG_WIDTH, MDTP_ERROR_MSG_HEIGHT);
+        if (NULL == fbimg)
+        {
+            dprintf(CRITICAL,"ERROR: failed to read error image from mmc\n");
+            return -1;
+        }
+
+        fbcon_putImage_in_location(fbimg, x, y);
+
+		return 0;
+	}
+	else
+	{
+	    dprintf(CRITICAL,"ERROR: fbcon_config struct is NULL\n");
+		return -1;
+	}
 }
 
-/* Show the "Firmware Invalid" image */
-static void display_image_on_screen_INVALID()
+/**
+ * Read from mmc the image in the given offset, of the given width and height.
+ * Then, display the image on the screen in the given (x,y) location.
+ */
+static void display_image(uint32_t offset, uint32_t width, uint32_t height, uint32_t x, uint32_t y)
 {
-	struct fbimage *fbimg;
+    struct mdtp_fbimage *fbimg;
 
-	fbcon_clear();
-	fbimg = mdtp_images_mmc_INVALID();
-	fbimg->header.width = MDTP_IMAGE_WIDTH;
-	fbimg->header.height = MDTP_IMAGE_HEIGHT;
+    if (fb_config)
+    {
+        fbimg = mdtp_read_mmc_image(offset, width, height);
+        if (NULL == fbimg)
+        {
+            dprintf(CRITICAL,"ERROR: failed to read image from mmc\n");
+            display_error_msg();
+        }
 
-	dprintf(CRITICAL, "display_image_on_screen_INVALID\n");
-	fbcon_putImage(fbimg, true);
+        fbcon_putImage_in_location(fbimg, x, y);
+    }
+    else
+    {
+        dprintf(CRITICAL,"ERROR: fbcon_config struct is NULL\n");
+        display_error_msg();
+    }
 }
 
-/* Show the "Verifying Firmware" image */
-static void display_image_on_screen_CHECKING()
+/**
+ * Display initial delay message
+ */
+static void display_initial_delay()
 {
-	struct fbimage *fbimg;
+    uint32_t x = CENTER_IMAGE_ON_X_AXIS(MDTP_MAIN_TEXT_WIDTH,fb_config->width);
+	uint32_t y = (fb_config->height)*MAIN_TEXT_RELATIVE_Y_LOCATION;
 
-	fbcon_clear();
-	fbimg = mdtp_images_mmc_CHECKING();
-	fbimg->header.width = MDTP_IMAGE_WIDTH;
-	fbimg->header.height = MDTP_IMAGE_HEIGHT;
-
-	dprintf(CRITICAL, "display_image_on_screen_CHECKING\n");
-	fbcon_putImage(fbimg, true);
+	display_image(MDTP_INITIAL_DELAY_OFFSET, MDTP_MAIN_TEXT_WIDTH, MDTP_MAIN_TEXT_HEIGHT, x, y);
 }
 
-/* Show the "Verifying Firmware" image */
-static void display_image_on_screen_RECOVERED()
+/**
+ * Display "enter PIN" message
+ */
+static void display_enter_pin()
 {
-	struct fbimage *fbimg;
+    uint32_t x = CENTER_IMAGE_ON_X_AXIS(MDTP_MAIN_TEXT_WIDTH,fb_config->width);
+	uint32_t y = (fb_config->height)*MAIN_TEXT_RELATIVE_Y_LOCATION;
 
-	fbcon_clear();
-	fbimg = mdtp_images_mmc_RECOVERED();
-	fbimg->header.width = MDTP_IMAGE_WIDTH;
-	fbimg->header.height = MDTP_IMAGE_HEIGHT;
-
-	dprintf(CRITICAL, "display_image_on_screen_RECOVERED\n");
-	fbcon_putImage(fbimg, true);
+	display_image(MDTP_ENTER_PIN_OFFSET, MDTP_MAIN_TEXT_WIDTH, MDTP_MAIN_TEXT_HEIGHT, x, y);
 }
 
-/* Display the "Firmware Valid" screen */
-void show_OK_msg()
+/**
+ * Display invalid PIN message
+ */
+static void display_invalid_pin()
 {
-	display_image_on_screen_OK();
-	mdelay(MDTP_UX_DELAY);
+    uint32_t x = CENTER_IMAGE_ON_X_AXIS(MDTP_MAIN_TEXT_WIDTH,fb_config->width);
+	uint32_t y = (fb_config->height)*MAIN_TEXT_RELATIVE_Y_LOCATION;
 
-	return;
+	display_image(MDTP_INVALID_PIN_OFFSET, MDTP_MAIN_TEXT_WIDTH, MDTP_MAIN_TEXT_HEIGHT, x, y);
 }
 
-/* Display the "Firmware Invalid" screen */
-void show_invalid_msg()
+/**
+ * Clear digits instructions
+ */
+static void display_digits_instructions()
 {
-	display_image_on_screen_INVALID();
+    uint32_t x = CENTER_IMAGE_ON_X_AXIS(MDTP_DIGITS_INSTRUCTIONS_WIDTH,fb_config->width);
+	uint32_t y = (fb_config->height)*PIN_TEXT_RELATIVE_Y_LOCATION;
+
+	display_image(MDTP_DIGITS_INSTRUCTIONS_OFFSET, MDTP_DIGITS_INSTRUCTIONS_WIDTH, MDTP_DIGITS_INSTRUCTIONS_HEIGHT, x, y);
+}
+
+/**
+ * Clear digits instructions
+ */
+static void clear_digits_instructions()
+{
+    uint32_t y = (fb_config->height)*PIN_TEXT_RELATIVE_Y_LOCATION;
+
+    fbcon_clear_section(y, MDTP_DIGITS_INSTRUCTIONS_HEIGHT);
+}
+
+/**
+ * Display a digit as un-selected.
+ */
+static void display_digit(uint32_t x, uint32_t y, uint32_t digit)
+{
+    display_image(MDTP_PIN_DIGIT_0_OFFSET + digit*MDTP_PIN_DIGITS_OFFSET,
+            MDTP_PIN_DIGIT_WIDTH, MDTP_PIN_DIGIT_HEIGHT, x, y);
+}
+
+/**
+ * Display a digit as selected.
+ */
+static void display_selected_digit(uint32_t x, uint32_t y, uint32_t digit)
+{
+    display_image(MDTP_PIN_SELECTED_DIGIT_0_OFFSET + digit*MDTP_PIN_DIGITS_OFFSET,
+			MDTP_PIN_DIGIT_WIDTH, MDTP_PIN_DIGIT_HEIGHT, x, y);
+}
+
+/**
+ * Display OK button as un-selected.
+ */
+static void display_ok_button()
+{
+    uint32_t ok_x = CENTER_IMAGE_ON_X_AXIS(MDTP_OK_BUTTON_WIDTH,fb_config->width);
+	uint32_t ok_y = (fb_config->height)*OK_BUTTON_RELATIVE_Y_LOCATION;
+
+	display_image(MDTP_OK_BUTTON_OFFSET, MDTP_OK_BUTTON_WIDTH, MDTP_OK_BUTTON_HEIGHT, ok_x, ok_y);
+}
+
+/**
+ * Display OK button as selected.
+ */
+static void display_selected_ok_button()
+{
+    uint32_t ok_x = CENTER_IMAGE_ON_X_AXIS(MDTP_OK_BUTTON_WIDTH,fb_config->width);
+	uint32_t ok_y = (fb_config->height)*OK_BUTTON_RELATIVE_Y_LOCATION;
+
+	display_image(MDTP_SELECTED_OK_BUTTON_OFFSET, MDTP_OK_BUTTON_WIDTH, MDTP_OK_BUTTON_HEIGHT,  ok_x, ok_y);
+}
+
+/**
+ * Display the instructions for the OK button.
+ */
+static void display_pin_instructions()
+{
+    uint32_t x = CENTER_IMAGE_ON_X_AXIS(MDTP_PIN_INSTRUCTIONS_WIDTH,fb_config->width);
+	uint32_t y = (fb_config->height)*OK_TEXT_RELATIVE_Y_LOCATION;
+
+	display_image(MDTP_PIN_INSTRUCTIONS_OFFSET, MDTP_PIN_INSTRUCTIONS_WIDTH, MDTP_PIN_INSTRUCTIONS_HEIGHT, x, y);
+}
+
+/**
+ * Clear the instructions for the OK button.
+ */
+static void clear_pin_message()
+{
+    uint32_t y = (fb_config->height)*OK_TEXT_RELATIVE_Y_LOCATION;
+
+	fbcon_clear_section(y, MDTP_PIN_INSTRUCTIONS_HEIGHT);
+}
+
+/**
+ * Display the basic layout of the screen (done only once).
+ */
+static void display_initial_screen(uint32_t pin_length)
+{
+    if (g_initial_screen_displayed == true)
+		return;
+
+    fb_config = fbcon_display();
+
+    if (fb_config)
+	{
+		fbcon_clear();
+
+		if (display_error_message())
+		    display_error_msg();
+		display_initial_delay();
+
+		mdelay(INITIAL_DELAY_MSECONDS);
+
+		g_pin_frames_y_location = ((fb_config->height)*PIN_RELATIVE_Y_LOCATION);
+
+		uint32_t total_pin_length = pin_length*MDTP_PIN_DIGIT_WIDTH + DIGIT_SPACE*(pin_length - 1);
+		uint32_t complete_pin_centered = (fb_config->width - total_pin_length)/2;
+
+		for (int i=0; i<(int)pin_length; i++)
+		{
+			g_pin_frames_x_location[i] = complete_pin_centered + i*(DIGIT_SPACE+MDTP_PIN_DIGIT_WIDTH);
+		}
+
+		for (int i=0; i<(int)pin_length; i++)
+		{
+			display_digit(g_pin_frames_x_location[i], g_pin_frames_y_location, 0);
+		}
+
+		display_ok_button();
+
+		g_initial_screen_displayed = true;
+	}
+	else
+	{
+	    dprintf(CRITICAL,"ERROR: fbcon_config struct is NULL\n");
+	    display_error_msg();
+		return;
+	}
+}
+
+/**
+ * Display the recovery PIN screen and set received buffer
+ * with the PIN the user has entered.
+ * The entered PIN will be validated by the calling function.
+ */
+static void display_get_pin_interface(char *entered_pin, uint32_t pin_length)
+{
+	uint32_t previous_position = 0, current_position = 0;
+
+	display_initial_screen(pin_length);
+	display_enter_pin();
+
+	// Convert ascii to digits
+	for (uint32_t i=0; i<pin_length; i++)
+	{
+		entered_pin[i] -= '0';
+	}
+	display_selected_digit(g_pin_frames_x_location[0], g_pin_frames_y_location, entered_pin[0]);
+	display_digits_instructions();
 
 	while (1)
 	{
-		if(target_volume_down())
+		if (target_volume_up())
 		{
-			display_image_on_screen_RECOVERED();
-			mdelay(MDTP_UX_DELAY);
-			break;
+			// current position is the OK button
+			if (current_position == pin_length)
+			{
+				// Convert digits to ascii and
+				// validate entered PIN in the calling function
+				for (uint32_t i=0; i<pin_length; i++)
+				{
+					entered_pin[i] += '0';
+				}
+				return;
+			}
+
+			// current position is a PIN slot
+			entered_pin[current_position] = (entered_pin[current_position]+1) % 10;
+			display_selected_digit(g_pin_frames_x_location[current_position], g_pin_frames_y_location, entered_pin[current_position]);
+			mdelay(MDTP_PRESSING_DELAY_MSEC);
+		}
+		if (target_volume_down())
+		{
+			previous_position = current_position;
+			current_position = (current_position+1) % (pin_length+1);
+
+			// previous position was the OK button
+			if (previous_position == pin_length)
+			{
+				clear_pin_message();
+				display_ok_button();
+
+				display_digits_instructions();
+				display_selected_digit(g_pin_frames_x_location[current_position], g_pin_frames_y_location, entered_pin[current_position]);
+
+			}
+
+			// current position is the OK button
+			else if (current_position == pin_length)
+			{
+				display_digit(g_pin_frames_x_location[previous_position], g_pin_frames_y_location, entered_pin[previous_position]);
+				clear_digits_instructions();
+
+				display_selected_ok_button();
+				display_pin_instructions();
+			}
+
+			// both the previous and the current positions are PIN slots
+			else
+			{
+				display_digit(g_pin_frames_x_location[previous_position], g_pin_frames_y_location, entered_pin[previous_position]);
+
+				display_selected_digit(g_pin_frames_x_location[current_position], g_pin_frames_y_location, entered_pin[current_position]);
+			}
+
+			mdelay(MDTP_PRESSING_DELAY_MSEC);
 		}
 	}
+}
+
+/*----------------------------------------------------------------------------
+ * External Functions
+ * -------------------------------------------------------------------------*/
+
+/**
+ * Display the recovery PIN screen and set received buffer
+ * with the PIN the user has entered.
+ */
+void get_pin_from_user(char *entered_pin, uint32_t pin_length)
+{
+	display_get_pin_interface(entered_pin, pin_length);
 
 	return;
 }
 
-/* Display the "Verifying Firmware" screen */
-void show_checking_msg()
+/**
+ * User has entered invalid PIN, display error message and
+ * allow the user to try again.
+ */
+void display_invalid_pin_msg()
 {
-	display_image_on_screen_CHECKING();
-	return;
+    clear_pin_message();
+    display_ok_button();
+
+    display_invalid_pin();
+
+    mdelay(INVALID_PIN_DELAY_MSECONDS);
 }
 
-/* Display the "Verifying Firmware" screen */
-void show_recovered_msg()
+/**
+ *  Display error message and stop boot process.
+ */
+void display_error_msg()
 {
-	display_image_on_screen_RECOVERED();
-	return;
+    fbcon_clear();
+	display_error_message();   // No point in checking the return value here
+
+	// Invalid state. Nothing to be done but contacting the OEM.
+	// Stop boot process.
+	dprintf(CRITICAL,"ERROR: blocking boot process\n");
+	while(1)
+	{
+
+	}
 }
 
diff --git a/target/msm8994/init.c b/target/msm8994/init.c
index 6dc9ce9..824bdc1 100644
--- a/target/msm8994/init.c
+++ b/target/msm8994/init.c
@@ -105,7 +105,7 @@
 }
 
 /* Return 1 if vol_up pressed */
-static int target_volume_up()
+int target_volume_up()
 {
 	uint8_t status = 0;
 	struct pm8x41_gpio gpio;