diff --git a/ui/common/discover-client.c b/ui/common/discover-client.c
index 8ad4611..ed51afc 100644
--- a/ui/common/discover-client.c
+++ b/ui/common/discover-client.c
@@ -452,3 +452,21 @@ int discover_client_send_plugin_install(struct discover_client *client,
 
 	return pb_protocol_write_message(client->fd, message);
 }
+
+int discover_client_send_firmware_update(struct discover_client *client,
+		char *url)
+{
+	struct pb_protocol_message *message;
+	int len;
+
+	len = pb_protocol_url_len(url);
+
+	message = pb_protocol_create_message(client,
+				PB_PROTOCOL_ACTION_FIRMWARE_UPDATE, len);
+	if (!message)
+		return -1;
+
+	pb_protocol_serialise_url(url, message->payload, len);
+
+	return pb_protocol_write_message(client->fd, message);
+}
diff --git a/ui/common/discover-client.h b/ui/common/discover-client.h
index 7224691..9352de1 100644
--- a/ui/common/discover-client.h
+++ b/ui/common/discover-client.h
@@ -102,4 +102,8 @@ int discover_client_send_url(struct discover_client *client, char *url);
 int discover_client_send_plugin_install(struct discover_client *client,
 		char *file);
 
+/* Send url to firmware image to the discover server */
+int discover_client_send_firmware_update(struct discover_client *client,
+		char *url);
+
 #endif
diff --git a/ui/ncurses/Makefile.am b/ui/ncurses/Makefile.am
index 40e11b8..4418961 100644
--- a/ui/ncurses/Makefile.am
+++ b/ui/ncurses/Makefile.am
@@ -55,7 +55,10 @@ ui_ncurses_libpbnc_la_SOURCES = \
 	ui/ncurses/nc-plugin.c \
 	ui/ncurses/nc-plugin.h \
 	ui/ncurses/nc-plugin-help.c \
-	ui/ncurses/nc-plugin-menu-help.c
+	ui/ncurses/nc-plugin-menu-help.c \
+	ui/ncurses/nc-firmware.c \
+	ui/ncurses/nc-firmware.h \
+	ui/ncurses/nc-firmware-help.c
 
 sbin_PROGRAMS += ui/ncurses/petitboot-nc
 
diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c
index 46b7839..835cef3 100644
--- a/ui/ncurses/nc-cui.c
+++ b/ui/ncurses/nc-cui.c
@@ -45,6 +45,7 @@
 #include "nc-statuslog.h"
 #include "nc-subset.h"
 #include "nc-plugin.h"
+#include "nc-firmware.h"
 
 extern const struct help_text main_menu_help_text;
 extern const struct help_text plugin_menu_help_text;
@@ -392,6 +393,13 @@ static void cui_plugin_menu_exit(struct pmenu *menu)
 	cui_set_current(cui, &cui->main->scr);
 }
 
+static void cui_firmware_exit(struct cui *cui)
+{
+	cui_set_current(cui, &cui->main->scr);
+	talloc_free(cui->firmware_screen);
+	cui->firmware_screen = NULL;
+}
+
 void cui_show_add_url(struct cui *cui)
 {
 	cui->add_url_screen = add_url_screen_init(cui, cui_add_url_exit);
@@ -410,6 +418,13 @@ void cui_show_plugin(struct pmenu_item *item)
 	cui_set_current(cui, plugin_screen_scr(cui->plugin_screen));
 }
 
+void cui_show_firmware(struct cui *cui)
+{
+	cui->firmware_screen = firmware_screen_init(cui, cui->config,
+			cui->sysinfo, cui_firmware_exit);
+	cui_set_current(cui, firmware_screen_scr(cui->firmware_screen));
+}
+
 static void cui_help_exit(struct cui *cui)
 {
 	cui_set_current(cui, help_screen_return_scr(cui->help_screen));
@@ -1018,6 +1033,44 @@ static void cui_update_mm_title(struct cui *cui)
 		nc_scr_post(cui->current);
 }
 
+static void cui_update_menu_item_name(struct cui *cui, const char *prefix,
+		const char *suffix)
+{
+	struct pmenu_item *item;
+	bool found = false;
+	unsigned int i;
+	char *label;
+
+	if (cui->current == &cui->main->scr)
+		nc_scr_unpost(cui->current);
+
+	if (set_menu_items(cui->main->ncm, NULL)) {
+		pb_log("%s: failed to unset menu items\n", __func__);
+		return;
+	}
+
+	for (i = 0 ; i < cui->main->item_count; i++) {
+		item = item_userptr(cui->main->items[i]);
+		if (strncmp(item->nci->name.str, prefix, strlen(prefix)))
+			continue;
+		label = talloc_asprintf(item, "%s %s", prefix, suffix);
+		pmenu_item_update(item, label);
+		talloc_free(label);
+		found = true;
+		break;
+	}
+
+	if (!found)
+		pb_log("Could not find item '%s' to apply suffix '%s'\n",
+				prefix, suffix);
+
+	if (set_menu_items(cui->main->ncm, cui->main->items))
+		pb_log("%s: failed to set menu items\n", __func__);
+
+	if (cui->current == &cui->main->scr)
+		nc_scr_post(cui->current);
+}
+
 static void cui_update_sysinfo(struct system_info *sysinfo, void *arg)
 {
 	struct cui *cui = cui_from_arg(arg);
@@ -1039,6 +1092,16 @@ static void cui_update_sysinfo(struct system_info *sysinfo, void *arg)
 	if (cui->boot_editor)
 		boot_editor_update(cui->boot_editor, sysinfo);
 
+	if (cui->firmware_screen)
+		firmware_screen_update(cui->firmware_screen, cui->config,
+				sysinfo);
+
+	/* Update the Firmware title if update occured */
+	if (sysinfo->update_status == UPDATE_COMPLETED)
+		cui_update_menu_item_name(cui, "Firmware Updates",
+				"(Update Applied!)");
+
+
 	cui_update_mm_title(cui);
 }
 
@@ -1085,6 +1148,10 @@ static void cui_update_config(struct config *config, void *arg)
 	if (cui->config_screen)
 		config_screen_update(cui->config_screen, config, cui->sysinfo);
 
+	if (cui->firmware_screen)
+		firmware_screen_update(cui->firmware_screen, config,
+				cui->sysinfo);
+
 	if (config->safe_mode)
 		nc_scr_status_printf(cui->current,
 				_("SAFE MODE: select '%s' to continue"),
@@ -1106,6 +1173,11 @@ int cui_send_plugin_install(struct cui *cui, char *file)
 	return discover_client_send_plugin_install(cui->client, file);
 }
 
+int cui_send_firmware_update(struct cui *cui, char *url)
+{
+	return discover_client_send_firmware_update(cui->client, url);
+}
+
 void cui_send_reinit(struct cui *cui)
 {
 	discover_client_send_reinit(cui->client);
@@ -1156,6 +1228,13 @@ static int menu_plugin_execute(struct pmenu_item *item)
 	return 0;
 }
 
+static int menu_firmware_execute(struct pmenu_item *item)
+{
+	if (cui_from_item(item)->client)
+		cui_show_firmware(cui_from_item(item));
+	return 0;
+}
+
 /**
  * pb_mm_init - Setup the main menu instance.
  */
@@ -1166,7 +1245,7 @@ static struct pmenu *main_menu_init(struct cui *cui)
 	int result;
 	bool lockdown = lockdown_active();
 
-	m = pmenu_init(cui, 9, cui_on_exit);
+	m = pmenu_init(cui, 10, cui_on_exit);
 	if (!m) {
 		pb_log("%s: failed\n", __func__);
 		return NULL;
@@ -1216,12 +1295,16 @@ static struct pmenu *main_menu_init(struct cui *cui)
 	i->on_execute = menu_plugin_execute;
 	pmenu_item_insert(m, i, 7);
 
+	i = pmenu_item_create(m, _("Firmware Updates"));
+	i->on_execute = menu_firmware_execute;
+	pmenu_item_insert(m, i, 8);
+
 	if (lockdown)
 		i = pmenu_item_create(m, _("Reboot"));
 	else
 		i = pmenu_item_create(m, _("Exit to shell"));
 	i->on_execute = pmenu_exit_cb;
-	pmenu_item_insert(m, i, 8);
+	pmenu_item_insert(m, i, 9);
 
 	result = pmenu_setup(m);
 
diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h
index b310f4a..711c7f8 100644
--- a/ui/ncurses/nc-cui.h
+++ b/ui/ncurses/nc-cui.h
@@ -72,6 +72,7 @@ struct cui {
 	struct help_screen *help_screen;
 	struct subset_screen *subset_screen;
 	struct statuslog_screen *statuslog_screen;
+	struct firmware_screen *firmware_screen;
 	struct pjs *pjs;
 	void *platform_info;
 	unsigned int default_item;
@@ -99,6 +100,8 @@ void cui_show_plugin_menu(struct cui *cui);
 int cui_send_config(struct cui *cui, struct config *config);
 int cui_send_url(struct cui *cui, char *url);
 int cui_send_plugin_install(struct cui *cui, char *file);
+void cui_show_firmware(struct cui *item);
+int cui_send_firmware_update(struct cui *cui, char *url);
 void cui_send_reinit(struct cui *cui);
 
 /* convenience routines */
diff --git a/ui/ncurses/nc-firmware-help.c b/ui/ncurses/nc-firmware-help.c
new file mode 100644
index 0000000..7b9d072
--- /dev/null
+++ b/ui/ncurses/nc-firmware-help.c
@@ -0,0 +1,4 @@
+#include "nc-helpscreen.h"
+
+struct help_text firmware_help_text = define_help_text("\
+Useful help text here");
diff --git a/ui/ncurses/nc-firmware.c b/ui/ncurses/nc-firmware.c
new file mode 100644
index 0000000..55fbcc9
--- /dev/null
+++ b/ui/ncurses/nc-firmware.c
@@ -0,0 +1,553 @@
+/*
+ *  Copyright (C) 2017 IBM Corporation
+ *
+ *  This program 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; version 2 of the License.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pb-config/pb-config.h>
+#include <talloc/talloc.h>
+#include <types/types.h>
+#include <i18n/i18n.h>
+#include <log/log.h>
+
+#include "nc-cui.h"
+#include "nc-firmware.h"
+#include "nc-widgets.h"
+#include "nc-menu.h"
+#include "ui/common/discover-client.h"
+#include "process/process.h"
+#include "system/system.h"
+
+#define N_FIELDS        15
+
+extern const struct help_text firmware_help_text;
+
+struct package_versions {
+	struct nc_widget_label	*name;
+	struct nc_widget_label	*current;
+	struct nc_widget_label	*update;
+};
+
+struct firmware_screen {
+	struct nc_scr		scr;
+	struct cui		*cui;
+	struct nc_widgetset	*widgetset;
+	WINDOW			*pad;
+
+	bool			exit;
+	bool			show_help;
+	bool			need_redraw;
+	bool			need_update;
+	void			(*on_exit)(struct cui *);
+
+	int			label_x;
+	int			field_x;
+	int			scroll_y;
+
+	unsigned int		n_versions;
+	int		current_x;
+	int		update_x;
+
+	struct {
+		struct nc_widget_label		*source_l;
+		struct nc_widget_textbox	*source_f;
+		struct nc_widget_button		*source_update_b;
+
+		struct nc_widget_label		*custom_l;
+		struct nc_widget_textbox	*custom_f;
+		struct nc_widget_button		*custom_update_b;
+
+		struct nc_widget_label		*package_l;
+		struct nc_widget_label		*current_l;
+		struct nc_widget_label		*update_l;
+		struct package_versions		*versions;
+
+		struct nc_widget_label		*status_l;
+		struct nc_widget_label		*status_f;
+		struct nc_widget_label		*safe_mode;
+
+		struct nc_widget_button		*help_b;
+		struct nc_widget_button		*cancel_b;
+	} widgets;
+};
+
+static struct firmware_screen *firmware_screen_from_scr(struct nc_scr *scr)
+{
+	struct firmware_screen *firmware_screen;
+
+	assert(scr->sig == pb_firmware_screen_sig);
+	firmware_screen = (struct firmware_screen *)
+		((char *)scr - (size_t)&((struct firmware_screen *)0)->scr);
+	assert(firmware_screen->scr.sig == pb_firmware_screen_sig);
+	return firmware_screen;
+}
+
+struct nc_scr *firmware_screen_scr(struct firmware_screen *screen)
+{
+	return &screen->scr;
+}
+
+static void pad_refresh(struct firmware_screen *screen)
+{
+	int y, x, rows, cols;
+
+	getmaxyx(screen->scr.sub_ncw, rows, cols);
+	getbegyx(screen->scr.sub_ncw, y, x);
+
+	prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
+}
+
+static void firmware_screen_widget_focus(struct nc_widget *widget, void *arg)
+{
+	struct firmware_screen *screen = arg;
+	int w_y, w_height, w_focus, s_max, adjust;
+
+	w_height = widget_height(widget);
+	w_focus = widget_focus_y(widget);
+	w_y = widget_y(widget) + w_focus;
+	s_max = getmaxy(screen->scr.sub_ncw) - 1;
+
+	if (w_y < screen->scroll_y)
+		screen->scroll_y = w_y;
+
+	else if (w_y + screen->scroll_y + 1 > s_max) {
+		/* Fit as much of the widget into the screen as possible */
+		adjust = min(s_max - 1, w_height - w_focus);
+		if (w_y + adjust >= screen->scroll_y + s_max)
+			screen->scroll_y = max(0, 1 + w_y + adjust - s_max);
+	} else
+		return;
+
+	pad_refresh(screen);
+}
+
+static void firmware_screen_process_key(struct nc_scr *scr, int key)
+{
+	struct firmware_screen *screen = firmware_screen_from_scr(scr);
+	bool handled;
+
+	pb_log("%s\n", __func__);
+
+	handled = widgetset_process_key(screen->widgetset, key);
+
+	if (!handled) {
+		pb_log("Not handled by widgetset\n");
+		switch (key) {
+		case 'x':
+		case 27: /* esc */
+			screen->exit = true;
+			break;
+		case 'h':
+			screen->show_help = true;
+			break;
+		}
+	}
+
+	if (screen->exit) {
+		screen->on_exit(screen->cui);
+
+	} else if (screen->show_help) {
+		screen->show_help = false;
+		screen->need_redraw = true;
+		cui_show_help(screen->cui, _("Firmware Updates"),
+				&firmware_help_text);
+
+	} else if (handled) {
+		pad_refresh(screen);
+	}
+}
+
+static void help_click(void *arg)
+{
+	struct firmware_screen *screen = arg;
+	screen->show_help = true;
+}
+
+static void cancel_click(void *arg)
+{
+	struct firmware_screen *screen = arg;
+	screen->exit = true;
+}
+
+static void update_source_click(void *arg)
+{
+	struct firmware_screen *screen = arg;
+	struct config *config;
+	char *url;
+	int rc;
+
+	config = config_copy(screen, screen->cui->config);
+
+	url = widget_textbox_get_value(screen->widgets.source_f);
+
+	talloc_free(config->metadata_source);
+	config->metadata_source = talloc_strdup(config, url);
+
+	config->safe_mode = false;
+
+	rc = cui_send_config(screen->cui, config);
+	talloc_free(config);
+
+	if (rc)
+		pb_log("nc-firmware: Failed to send config!\n");
+
+	screen->exit = true;
+}
+
+static void update_custom_click(void *arg)
+{
+	struct firmware_screen *screen = arg;
+	char *url = widget_textbox_get_value(screen->widgets.custom_f);
+	cui_send_firmware_update(screen->cui, url);
+	screen->exit = true;
+}
+
+static void firmware_screen_setup_empty(struct firmware_screen *screen,
+		const struct config *config, const struct system_info *sysinfo)
+{
+	if (sysinfo && !sysinfo->update_support)
+		widget_new_label(screen->widgetset, 2, screen->field_x,
+				_("Firmware update not supported on this platform"));
+	else if (config && !config->platform_update)
+		widget_new_label(screen->widgetset, 2, screen->field_x,
+				_("Firmware update not enabled on this platform"));
+	else
+		widget_new_label(screen->widgetset, 2, screen->field_x,
+				_("Waiting for configuration data..."));
+	screen->widgets.cancel_b = widget_new_button(screen->widgetset,
+			4, screen->field_x, 9, _("Cancel"),
+			cancel_click, screen);
+}
+
+static void firmware_screen_setup_widgets(struct firmware_screen *screen,
+		const struct config *config, const struct system_info *sysinfo)
+{
+	struct nc_widgetset *set = screen->widgetset;
+	int len_package, len_current;
+	unsigned int i, j;
+	char *label;
+
+	build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
+			== N_FIELDS);
+
+	screen->widgets.source_l = widget_new_label(set, 0, 0,
+			_("Metadata source:"));
+	screen->widgets.source_f = widget_new_textbox(set, 0, 0,
+			COLS - screen->field_x - 10, config->metadata_source);
+
+	screen->widgets.source_update_b = widget_new_button(set, 0, 0, 40,
+			_("Set and check remote metadata file"),
+			update_source_click, screen);
+
+	screen->widgets.custom_l = widget_new_label(set, 0, 0, "Update File:");
+	screen->widgets.custom_f = widget_new_textbox(set, 0, 0,
+			COLS - screen->field_x - 10, config->update_source);
+
+	screen->widgets.custom_update_b = widget_new_button(set, 0, 0, 30,
+			_("Update firmware"),
+			update_custom_click, screen);
+
+	label = talloc_strdup(screen,
+			update_status_display_name(sysinfo->update_status));
+	screen->widgets.status_l = widget_new_label(set, 0, 0,
+			_("Update Status:"));
+	screen->widgets.status_f = widget_new_label(set, 0, 0, label);
+
+	screen->widgets.package_l = widget_new_label(set, 0, 0,
+			_("Packages"));
+	screen->widgets.current_l = widget_new_label(set, 0, 0,
+			_("Current"));
+	screen->widgets.update_l = widget_new_label(set, 0, 0,
+			_("Update"));
+	screen->widgets.versions = talloc_array(screen,
+			struct package_versions, sysinfo->n_primary);
+	if (!screen->widgets.versions) {
+		pb_log("Failed to alloc versions array!\n");
+		goto skip_versions;
+	}
+
+	screen->n_versions = sysinfo->n_primary;
+	len_package = len_current = 0;
+	/* Create current version package name and version labels */
+	for (i = 0; i < sysinfo->n_primary; i++) {
+		screen->widgets.versions[i].name = widget_new_label(set,
+				0, 0, sysinfo->platform_primary[i].name);
+		widget_set_selectable(widget_label_base(
+					screen->widgets.versions[i].name),
+				true);
+		screen->widgets.versions[i].current = widget_new_label(set,
+				0, 0, sysinfo->platform_primary[i].version);
+		screen->widgets.versions[i].update = NULL;
+		if (strncols(sysinfo->platform_primary[i].name) > len_package)
+			len_package = strncols(sysinfo->platform_primary[i].name);
+		if (strncols(sysinfo->platform_primary[i].version) > len_current)
+			len_current = strncols(sysinfo->platform_primary[i].version);
+	}
+
+	screen->current_x = screen->label_x + len_package + 2;
+	screen->update_x += screen->current_x + len_current + 2;
+
+	/* Create update version labels, checking for missing packages */
+	for (i = 0; i < sysinfo->n_new; i++) {
+		for (j = 0; j < sysinfo->n_primary; j++) {
+			if ((strncmp(sysinfo->platform_primary[j].name,
+					sysinfo->platform_new[i].name,
+					strlen(sysinfo->platform_new[i].name)) == 0) &&
+					(strlen(sysinfo->platform_primary[j].name) ==
+					strlen(sysinfo->platform_new[i].name)))
+				break;
+		}
+		if (j < sysinfo->n_primary) {
+			/* Corresponding current exists */
+			screen->widgets.versions[j].update = widget_new_label(set,
+					0, 0, sysinfo->platform_new[i].version);
+			continue;
+		}
+
+		/* Package exists only in update versions */
+		screen->widgets.versions = talloc_realloc(screen,
+				screen->widgets.versions,
+				struct package_versions,
+				j + 1);
+		if (!screen->widgets.versions) {
+			pb_log("Failed to alloc new update package!\n");
+			break;
+		}
+		screen->n_versions++;
+		screen->widgets.versions[j].name = widget_new_label(set,
+				0, 0, sysinfo->platform_new[i].name);
+		widget_set_selectable(widget_label_base(
+					screen->widgets.versions[j].name),
+				true);
+		screen->widgets.versions[j].current = NULL;
+		screen->widgets.versions[j].update = widget_new_label(set,
+				0, 0, sysinfo->platform_new[i].version);
+	}
+
+skip_versions:
+	if (config->safe_mode)
+		screen->widgets.safe_mode = widget_new_label(set, 0, 0,
+			 _("Selecting either option will exit safe mode"));
+
+	screen->widgets.help_b = widget_new_button(set, 0, 0, 10,
+			_("Help"), help_click, screen);
+	screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10,
+			_("Cancel"), cancel_click, screen);
+}
+
+static int layout_pair(struct firmware_screen *screen, int y,
+		struct nc_widget_label *label,
+		struct nc_widget *field)
+{
+	struct nc_widget *label_w = widget_label_base(label);
+	widget_move(label_w, y, screen->label_x);
+	widget_move(field, y, screen->field_x);
+	return max(widget_height(label_w), widget_height(field));
+}
+
+static void firmware_screen_layout_widgets(struct firmware_screen *screen)
+{
+	unsigned int y, i;
+
+	y = 1;
+
+	y += layout_pair(screen, y, screen->widgets.source_l,
+			widget_textbox_base(screen->widgets.source_f));
+	widget_move(widget_button_base(screen->widgets.source_update_b), y++,
+			screen->field_x);
+
+	y +=1 ;
+
+	y += layout_pair(screen, y, screen->widgets.custom_l,
+			widget_textbox_base(screen->widgets.custom_f));
+	widget_move(widget_button_base(screen->widgets.custom_update_b), y++,
+			screen->field_x);
+
+	y +=1 ;
+
+	y += layout_pair(screen, y, screen->widgets.status_l,
+			widget_label_base(screen->widgets.status_f));
+
+	y += 1;
+
+	widget_move(widget_label_base(screen->widgets.package_l), y,
+				screen->label_x);
+	widget_move(widget_label_base(screen->widgets.current_l), y,
+				screen->current_x);
+	widget_move(widget_label_base(screen->widgets.update_l), y,
+				screen->update_x);
+
+	y += 1;
+	for (i = 0; i < screen->n_versions; i++) {
+		widget_move(widget_label_base(screen->widgets.versions[i].name),
+					y, screen->label_x);
+		if (screen->widgets.versions[i].current)
+			widget_move(widget_label_base(
+					screen->widgets.versions[i].current),
+				y, screen->current_x);
+		if (screen->widgets.versions[i].update)
+			widget_move(widget_label_base(
+					screen->widgets.versions[i].update),
+				y, screen->update_x);
+		y += 1;
+
+	}
+
+	y += 1;
+
+	if (screen->cui->config->safe_mode) {
+		widget_move(widget_label_base(screen->widgets.safe_mode),
+			y, screen->label_x);
+		widget_set_visible(widget_label_base(screen->widgets.safe_mode),
+					true);
+		y += 1;
+	}
+
+	y += 1;
+
+	widget_move(widget_button_base(screen->widgets.help_b), y,
+		screen->field_x);
+	widget_move(widget_button_base(screen->widgets.cancel_b), y,
+		screen->field_x + 20);
+
+	y += 1;
+}
+
+static void firmware_screen_draw(struct firmware_screen *screen,
+		const struct config *config, const struct system_info *sysinfo)
+{
+	bool repost = false;
+	int height;
+
+	height = N_FIELDS + 6 + (sysinfo->n_primary + 2) + (sysinfo->n_new + 2);
+	if (!screen->pad || getmaxy(screen->pad) < height) {
+		if (screen->pad)
+			delwin(screen->pad);
+		screen->pad = newpad(height, COLS);
+	}
+
+	if (screen->widgetset) {
+		widgetset_unpost(screen->widgetset);
+		talloc_free(screen->widgetset);
+		repost = true;
+	}
+
+	screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
+			screen->pad);
+	widgetset_set_widget_focus(screen->widgetset,
+			firmware_screen_widget_focus, screen);
+
+	if (!config || !sysinfo || !sysinfo->update_support ||
+			!config->platform_update) {
+		firmware_screen_setup_empty(screen, config, sysinfo);
+	} else {
+		firmware_screen_setup_widgets(screen, config, sysinfo);
+		firmware_screen_layout_widgets(screen);
+	}
+
+	if (repost)
+		widgetset_post(screen->widgetset);
+}
+
+void firmware_screen_update(struct firmware_screen *screen,
+		const struct config *config, const struct system_info *sysinfo)
+{
+	if (screen->cui->current != firmware_screen_scr(screen)) {
+		screen->need_update = true;
+		return;
+	}
+
+	firmware_screen_draw(screen, config, sysinfo);
+	pad_refresh(screen);
+}
+
+static int firmware_screen_post(struct nc_scr *scr)
+{
+	struct firmware_screen *screen = firmware_screen_from_scr(scr);
+
+	widgetset_post(screen->widgetset);
+
+	nc_scr_frame_draw(scr);
+	if (screen->need_redraw) {
+		redrawwin(scr->main_ncw);
+		screen->need_redraw = false;
+	}
+	wrefresh(screen->scr.main_ncw);
+	pad_refresh(screen);
+	return 0;
+}
+
+static int firmware_screen_unpost(struct nc_scr *scr)
+{
+	widgetset_unpost(firmware_screen_from_scr(scr)->widgetset);
+	return 0;
+}
+
+static void firmware_screen_resize(struct nc_scr *scr)
+{
+	/* FIXME: forms can't be resized, need to recreate here */
+	firmware_screen_unpost(scr);
+	firmware_screen_post(scr);
+}
+
+static int firmware_screen_destroy(void *arg)
+{
+	struct firmware_screen *screen = arg;
+	if (screen->pad)
+		delwin(screen->pad);
+	return 0;
+}
+
+struct firmware_screen *firmware_screen_init(struct cui *cui,
+		const struct config *config,
+		const struct system_info *sysinfo,
+		void (*on_exit)(struct cui *))
+{
+	struct firmware_screen *screen = talloc_zero(cui, struct firmware_screen);
+	talloc_set_destructor(screen, firmware_screen_destroy);
+
+	nc_scr_init(&screen->scr, pb_firmware_screen_sig, 0,
+			cui, firmware_screen_process_key,
+			firmware_screen_post, firmware_screen_unpost,
+			firmware_screen_resize);
+
+
+	screen->cui = cui;
+	screen->on_exit = on_exit;
+	screen->label_x = 2;
+	screen->field_x = 25;
+	screen->need_redraw = false;
+
+	screen->scr.frame.ltitle = talloc_strdup(screen,
+			_("Firmware Updates"));
+	screen->scr.frame.rtitle = NULL;
+	screen->scr.frame.help = talloc_strdup(screen,
+			_("tab=next, shift+tab=previous, x=exit, h=help"));
+	nc_scr_frame_draw(&screen->scr);
+
+	scrollok(screen->scr.sub_ncw, true);
+
+	firmware_screen_draw(screen, config, sysinfo);
+
+	return screen;
+}
+
diff --git a/ui/ncurses/nc-firmware.h b/ui/ncurses/nc-firmware.h
new file mode 100644
index 0000000..38891c4
--- /dev/null
+++ b/ui/ncurses/nc-firmware.h
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (C) 2016 IBM Corporation
+ *
+ *  This program 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; version 2 of the License.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _NC_FIRMWARE_H
+#define _NC_FIRMWARE_H
+
+#include "nc-cui.h"
+
+struct firmware_screen;
+
+struct firmware_screen *firmware_screen_init(struct cui *cui,
+		const struct config *config,
+		const struct system_info *sysinfo,
+                void (*on_exit)(struct cui *));
+
+struct nc_scr *firmware_screen_scr(struct firmware_screen *screen);
+void firmware_screen_update(struct firmware_screen *screen,
+		const struct config *config, const struct system_info *sysinfo);
+
+#endif /* defined _NC_FIRMWARE_H */
diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h
index 5671a6b..35ede11 100644
--- a/ui/ncurses/nc-scr.h
+++ b/ui/ncurses/nc-scr.h
@@ -50,6 +50,7 @@ enum pb_nc_sig {
 	pb_add_url_screen_sig	= 888,
 	pb_subset_screen_sig	= 101,
 	pb_plugin_screen_sig	= 202,
+	pb_firmware_screen_sig	= 303,
 	pb_removed_sig		= -999,
 };
 
diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c
index 15cec80..7a318cb 100644
--- a/ui/ncurses/nc-widgets.c
+++ b/ui/ncurses/nc-widgets.c
@@ -194,7 +194,6 @@ static int label_destructor(void *ptr)
 	return 0;
 }
 
-
 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
 		int y, int x, char *str)
 {
@@ -1337,3 +1336,11 @@ int widget_focus_y(struct nc_widget *widget)
 	return widget->focus_y;
 }
 
+void widget_set_selectable(struct nc_widget *widget, bool selectable)
+{
+	if (selectable)
+		field_opts_on(widget->field, O_ACTIVE);
+	else
+		field_opts_off(widget->field, O_ACTIVE);
+}
+
diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h
index 4b67da7..7d7e8b5 100644
--- a/ui/ncurses/nc-widgets.h
+++ b/ui/ncurses/nc-widgets.h
@@ -86,6 +86,7 @@ int widget_width(struct nc_widget *widget);
 int widget_y(struct nc_widget *widget);
 int widget_x(struct nc_widget *widget);
 int widget_focus_y(struct nc_widget *widget);
+void widget_set_selectable(struct nc_widget *widget, bool selectable);
 
 /* widgetset API */
 typedef void (*widget_focus_cb)(struct nc_widget *widget, void *arg);
