Merge branch 'error-mem-viewer' into dev
* This is a preliminary version of hte error memory viewer
This commit is contained in:
commit
9c37e083a0
3
error-mem-viewer/.gitignore
vendored
Normal file
3
error-mem-viewer/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.ui~
|
||||||
|
*.user
|
||||||
|
*.user*
|
26
error-mem-viewer/CMakeLists.txt
Normal file
26
error-mem-viewer/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
project(error-mem-viewer)
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
pkg_search_module(GLIB REQUIRED glib-2.0)
|
||||||
|
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
|
||||||
|
pkg_check_modules(GTK_MOD REQUIRED gmodule-2.0)
|
||||||
|
|
||||||
|
aux_source_directory("src" SRCES)
|
||||||
|
include_directories("include" ${GLIB_INCLUDE_DIRS} ${GTK3_INCLUDE_DIRS} ${GTK_MOD_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
add_subdirectory(resources)
|
||||||
|
|
||||||
|
set(SOURCE_GENERATED
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/resources/resources.c
|
||||||
|
)
|
||||||
|
SET_SOURCE_FILES_PROPERTIES(${SOURCE_GENERATED} PROPERTIES GENERATED 1)
|
||||||
|
|
||||||
|
add_compile_options(-Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
|
||||||
|
|
||||||
|
link_directories(${GLIB_LINK_DIRS} ${GTK3_LINK_DIRS} ${GTK_MOD_LINK_DIRS})
|
||||||
|
add_definitions(${GLIB2_CFLAGS_OTHER} ${GTK_MOD_CFLAGS_OTHER})
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${SRCES} ${SOURCE_GENERATED})
|
||||||
|
target_link_libraries(${PROJECT_NAME} ${GLIB_LDFLAGS} ${GTK3_LDFLAGS} ${GTK_MOD_LDFLAGS})
|
||||||
|
add_dependencies(${PROJECT_NAME} glib-resources)
|
10
error-mem-viewer/include/error-mem-viewer/crc.h
Normal file
10
error-mem-viewer/include/error-mem-viewer/crc.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _CRC_H_
|
||||||
|
#define _CRC_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
uint32_t calculate_stm_crc(uint32_t *data, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _CRC_H_ */
|
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_
|
||||||
|
#define _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(ErrMemViewHexCellRenderer, err_mem_view_hex_cell_renderer, ERR_MEM_VIEW, HEX_CELL_RENDERER, GtkCellRendererText)
|
||||||
|
#define ERR_MEM_VIEW_TYPE_HEX_CELL_RENDERER (err_mem_view_address_cell_renderer_get_type())
|
||||||
|
|
||||||
|
GtkCellRenderer *err_mem_view_hex_cell_renderer_new();
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* _ERR_MEM_VIEWER_ADDRESS_CELL_RENDERER_H_ */
|
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef _SAFETY_MEM_TYPES_H_
|
||||||
|
#define _SAFETY_MEM_TYPES_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Magic number to signal a valid safety memory header.
|
||||||
|
*/
|
||||||
|
#define SAFETY_MEMORY_MAGIC 0x12AA5CB7
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error memory NOP entry
|
||||||
|
*/
|
||||||
|
#define SAFETY_MEMORY_NOP_ENTRY 0xC1AA1222
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Safety memory header
|
||||||
|
*/
|
||||||
|
struct safety_memory_header {
|
||||||
|
uint32_t magic; /**< @brief Magic. Set to @ref SAFETY_MEMORY_MAGIC */
|
||||||
|
uint32_t boot_status_offset; /**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/
|
||||||
|
uint32_t config_overrides_offset; /**< @brief Offset address of override entries */
|
||||||
|
uint32_t config_overrides_len; /**< @brief Length of override entries in words */
|
||||||
|
uint32_t err_memory_offset; /**< @brief Offset of the error memory */
|
||||||
|
uint32_t err_memory_end; /**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */
|
||||||
|
uint32_t crc; /**< @brief CRC of the header */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct safety_memory_boot_status {
|
||||||
|
/**
|
||||||
|
* @brief Reboot into the bootloader
|
||||||
|
*
|
||||||
|
* When this flag is set, the controller will load the bootloader to
|
||||||
|
* memory and execute it.
|
||||||
|
*/
|
||||||
|
uint32_t reboot_to_bootloader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bootloader has updated the code
|
||||||
|
*
|
||||||
|
* This flag is set, if the firmware ahs been updated successfully
|
||||||
|
*/
|
||||||
|
uint32_t code_updated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief reset_from_panic
|
||||||
|
*
|
||||||
|
* This flag is set, when entering the panic mode.
|
||||||
|
* Because the panic mode is reset by a watchdog reset,
|
||||||
|
* this flag is needed, in order to ensure, that the panic is handled correcly after
|
||||||
|
* the watchdog reset.
|
||||||
|
*/
|
||||||
|
uint32_t reset_from_panic;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _SAFETY_MEM_TYPES_H_ */
|
9
error-mem-viewer/resources/CMakeLists.txt
Normal file
9
error-mem-viewer/resources/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
add_custom_target(glib-resources DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/resources.c")
|
||||||
|
add_custom_command(DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.ui"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/resources.xml"
|
||||||
|
OUTPUT
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/resources.c"
|
||||||
|
COMMAND
|
||||||
|
glib-compile-resources --target="${CMAKE_CURRENT_BINARY_DIR}/resources.c" --sourcedir="${CMAKE_CURRENT_SOURCE_DIR}" --generate-source "${CMAKE_CURRENT_SOURCE_DIR}/resources.xml"
|
||||||
|
)
|
120
error-mem-viewer/resources/main-window.ui
Normal file
120
error-mem-viewer/resources/main-window.ui
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.38.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.24"/>
|
||||||
|
<object class="GtkWindow" id="main-window">
|
||||||
|
<property name="name">main-window</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkInfoBar" id="info-bar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="message-type">warning</property>
|
||||||
|
<property name="show-close-button">True</property>
|
||||||
|
<property name="revealed">False</property>
|
||||||
|
<signal name="response" handler="info_bar_close_cb" swapped="no"/>
|
||||||
|
<child internal-child="action_area">
|
||||||
|
<object class="GtkButtonBox">
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<property name="layout-style">end</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child internal-child="content_area">
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="spacing">16</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="shadow-type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="error-mem-tree-view">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<child internal-child="selection">
|
||||||
|
<object class="GtkTreeSelection"/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="titlebar">
|
||||||
|
<object class="GtkHeaderBar" id="header-bar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Error Memory Viewer</property>
|
||||||
|
<property name="subtitle" translatable="yes">Reflow Controller Error Memory Viewer</property>
|
||||||
|
<property name="show-close-button">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="open-button">
|
||||||
|
<property name="label">gtk-open</property>
|
||||||
|
<property name="name">open-button</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
|
<property name="use-stock">True</property>
|
||||||
|
<property name="always-show-image">True</property>
|
||||||
|
<signal name="clicked" handler="open_button_clicked_cb" swapped="no"/>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
6
error-mem-viewer/resources/resources.xml
Normal file
6
error-mem-viewer/resources/resources.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/gui">
|
||||||
|
<file compressed="true" alias="main">main-window.ui</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
26
error-mem-viewer/src/crc.c
Normal file
26
error-mem-viewer/src/crc.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include <error-mem-viewer/crc.h>
|
||||||
|
|
||||||
|
uint32_t do_crc(uint32_t init, uint32_t data)
|
||||||
|
{
|
||||||
|
uint32_t crc = init;
|
||||||
|
uint32_t cnt;
|
||||||
|
|
||||||
|
for(cnt=0; cnt < 32; cnt++)
|
||||||
|
{
|
||||||
|
crc = ((int32_t)(crc ^ data))<0 ? (crc << 1) ^ 0x04C11DB7 : crc << 1;
|
||||||
|
data <<=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t calculate_stm_crc(uint32_t *data, size_t len)
|
||||||
|
{
|
||||||
|
uint32_t crc = ~0U;
|
||||||
|
|
||||||
|
while(len--) {
|
||||||
|
crc = do_crc(crc, *data++);
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
81
error-mem-viewer/src/err-mem-viewer-hex-cell-renderer.c
Normal file
81
error-mem-viewer/src/err-mem-viewer-hex-cell-renderer.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include <error-mem-viewer/err-mem-viewer-hex-cell-renderer.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct _ErrMemViewHexCellRenderer {
|
||||||
|
GtkCellRendererText base;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_HEXNUM = 1,
|
||||||
|
PROP_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(ErrMemViewHexCellRenderer, err_mem_view_address_cell_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
|
||||||
|
|
||||||
|
static void err_mem_view_address_cell_renderer_init(ErrMemViewHexCellRenderer *self)
|
||||||
|
{
|
||||||
|
(void)self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_gvalue_uint_to_string(const GValue *in, GValue *out)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
char text[32];
|
||||||
|
|
||||||
|
if (!in || !out)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_value_init(out, G_TYPE_STRING);
|
||||||
|
|
||||||
|
val = g_value_get_uint(in);
|
||||||
|
snprintf(text, sizeof(text), "0x%08X", val);
|
||||||
|
g_value_set_string(out, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void err_mem_view_address_cell_renderer_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
GValue val = G_VALUE_INIT;
|
||||||
|
|
||||||
|
switch (param_id) {
|
||||||
|
case PROP_HEXNUM:
|
||||||
|
convert_gvalue_uint_to_string(value, &val);
|
||||||
|
g_object_set_property(obj, "text", &val);
|
||||||
|
g_value_unset(&val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void err_mem_view_address_cell_renderer_get_property(GObject *object,
|
||||||
|
guint param_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
(void)value;
|
||||||
|
|
||||||
|
switch (param_id) {
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GParamSpec *properties[PROP_COUNT];
|
||||||
|
|
||||||
|
void err_mem_view_address_cell_renderer_class_init(ErrMemViewHexCellRendererClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *oclass = G_OBJECT_CLASS(klass);
|
||||||
|
|
||||||
|
oclass->set_property = err_mem_view_address_cell_renderer_set_property;
|
||||||
|
oclass->get_property = err_mem_view_address_cell_renderer_get_property;
|
||||||
|
|
||||||
|
properties[PROP_HEXNUM] = g_param_spec_uint("hex-num", "hex-num", "Hex number to display", 0U, UINT_MAX, 0U, G_PARAM_WRITABLE);
|
||||||
|
g_object_class_install_properties(oclass, PROP_COUNT, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkCellRenderer *err_mem_view_hex_cell_renderer_new()
|
||||||
|
{
|
||||||
|
return GTK_CELL_RENDERER(g_object_new(ERR_MEM_VIEW_TYPE_HEX_CELL_RENDERER, NULL));
|
||||||
|
}
|
537
error-mem-viewer/src/main.c
Normal file
537
error-mem-viewer/src/main.c
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <error-mem-viewer/err-mem-viewer-hex-cell-renderer.h>
|
||||||
|
#include <error-mem-viewer/safety-memory-types.h>
|
||||||
|
#include <error-mem-viewer/crc.h>
|
||||||
|
|
||||||
|
enum memory_view_columns {
|
||||||
|
MEM_VIEW_COL_ADDRESS = 0,
|
||||||
|
MEM_VIEW_COL_DATA,
|
||||||
|
MEM_VIEW_COL_INTERPRETATION,
|
||||||
|
MEM_VIEW_COLOR,
|
||||||
|
MEM_VIEW_COLUMN_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct application_data {
|
||||||
|
GtkWidget *borrowed_main_window;
|
||||||
|
GtkTreeView *borrowed_tree_view;
|
||||||
|
GtkHeaderBar *borrowed_header_bar;
|
||||||
|
GtkInfoBar *borrowed_info_bar;
|
||||||
|
const char *error_memory_data;
|
||||||
|
size_t file_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum entry_state {
|
||||||
|
ENTRY_STATE_VALID = 0,
|
||||||
|
ENTRY_STATE_INVALID,
|
||||||
|
ENTRY_STATE_NOT_PARSED,
|
||||||
|
ENTRY_STATE_BLOCK
|
||||||
|
};
|
||||||
|
|
||||||
|
static void append_data(GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, GString *interpretation, enum entry_state state, uint32_t addr, uint32_t val)
|
||||||
|
{
|
||||||
|
const GdkRGBA invalid_color = {
|
||||||
|
.red = 1.0,
|
||||||
|
.alpha = 1.0,
|
||||||
|
.green = 0.0,
|
||||||
|
.blue = 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GdkRGBA valid_color = {
|
||||||
|
.red = 0.0,
|
||||||
|
.alpha = 1.0,
|
||||||
|
.green = 1.0,
|
||||||
|
.blue = 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GdkRGBA warn_color = {
|
||||||
|
.red = 1.0,
|
||||||
|
.alpha = 1.0,
|
||||||
|
.green = 1.0,
|
||||||
|
.blue = 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GdkRGBA block_color = {
|
||||||
|
.red = 1.0,
|
||||||
|
.alpha = 1.0,
|
||||||
|
.green = 0.3,
|
||||||
|
.blue = 0.5,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GdkRGBA *col;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case ENTRY_STATE_VALID:
|
||||||
|
col = &valid_color;
|
||||||
|
break;
|
||||||
|
case ENTRY_STATE_NOT_PARSED:
|
||||||
|
col = &warn_color;
|
||||||
|
break;
|
||||||
|
case ENTRY_STATE_BLOCK:
|
||||||
|
col = &block_color;
|
||||||
|
break;
|
||||||
|
case ENTRY_STATE_INVALID:
|
||||||
|
default:
|
||||||
|
col = &invalid_color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_tree_store_append(store, iter, parent);
|
||||||
|
gtk_tree_store_set(store, iter, MEM_VIEW_COL_ADDRESS, addr, MEM_VIEW_COLOR, col, MEM_VIEW_COL_INTERPRETATION, (interpretation ? interpretation->str : ""), MEM_VIEW_COL_DATA, val, -1);
|
||||||
|
if (interpretation)
|
||||||
|
g_string_free(interpretation, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void info_bar_show_message(GtkInfoBar *info_bar, GtkMessageType type, const char *format, ...)
|
||||||
|
{
|
||||||
|
GString *string;
|
||||||
|
va_list args;
|
||||||
|
GtkWidget *content;
|
||||||
|
GList *children, *iter;
|
||||||
|
GtkWidget *label;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
string = g_string_new(NULL);
|
||||||
|
|
||||||
|
g_string_vprintf(string, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
content = gtk_info_bar_get_content_area(info_bar);
|
||||||
|
children = gtk_container_get_children(GTK_CONTAINER(content));
|
||||||
|
for (iter = children; iter; iter = g_list_next(iter))
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(iter->data));
|
||||||
|
g_list_free(children);
|
||||||
|
|
||||||
|
label = gtk_label_new(string->str);
|
||||||
|
gtk_container_add(GTK_CONTAINER(content), label);
|
||||||
|
gtk_widget_show(label);
|
||||||
|
gtk_info_bar_set_message_type(info_bar, type);
|
||||||
|
gtk_widget_show(GTK_WIDGET(info_bar));
|
||||||
|
gtk_info_bar_set_revealed(info_bar, TRUE);
|
||||||
|
|
||||||
|
g_string_free(string, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_err_mem_header(struct safety_memory_header *header, uint32_t *expected)
|
||||||
|
{
|
||||||
|
uint32_t crc;
|
||||||
|
|
||||||
|
crc = calculate_stm_crc((uint32_t *)header, sizeof(*header) / sizeof(uint32_t) - 1);
|
||||||
|
if (expected)
|
||||||
|
*expected = crc;
|
||||||
|
if (crc == header->crc)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GString *new_string_printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
GString *string;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
string = g_string_new(NULL);
|
||||||
|
g_string_vprintf(string, fmt, args);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_error_memory(GtkTreeView *tree_view, const unsigned char *memory, size_t len)
|
||||||
|
{
|
||||||
|
GtkTreeStore *store;
|
||||||
|
GtkTreeIter iter;
|
||||||
|
GtkTreeIter iter2;
|
||||||
|
GtkTreeIter *parent_iter, *child_iter;
|
||||||
|
enum entry_state state;
|
||||||
|
GString *interpret;
|
||||||
|
bool valid;
|
||||||
|
bool analyze_further = true;
|
||||||
|
struct safety_memory_header header;
|
||||||
|
unsigned int i;
|
||||||
|
uint32_t dat, expected_value;
|
||||||
|
|
||||||
|
store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
|
||||||
|
gtk_tree_store_clear(store);
|
||||||
|
|
||||||
|
for (i = 0; i < len / 4; i++) {
|
||||||
|
valid = false;
|
||||||
|
interpret = NULL;
|
||||||
|
dat = memory[i*4];
|
||||||
|
dat |= ((uint32_t)memory[i*4+1]) << 8;
|
||||||
|
dat |= ((uint32_t)memory[i*4+2]) << 16;
|
||||||
|
dat |= ((uint32_t)memory[i*4+3]) << 24;
|
||||||
|
|
||||||
|
state = ENTRY_STATE_NOT_PARSED;
|
||||||
|
|
||||||
|
if (!analyze_further) {
|
||||||
|
interpret = g_string_new("Not analyzed due to previous error");
|
||||||
|
state = ENTRY_STATE_NOT_PARSED;
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
/* Parse header */
|
||||||
|
switch (i) {
|
||||||
|
case 0: /* Magic */
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
append_data(store, parent_iter, child_iter, new_string_printf("Header"), ENTRY_STATE_BLOCK, i * 4, dat);
|
||||||
|
parent_iter = child_iter;
|
||||||
|
child_iter = &iter2;
|
||||||
|
if (dat == SAFETY_MEMORY_MAGIC) {
|
||||||
|
valid = true;
|
||||||
|
interpret = g_string_new("Valid Header Magic");
|
||||||
|
} else {
|
||||||
|
interpret = g_string_new("Invalid Header Magic");
|
||||||
|
analyze_further = false;
|
||||||
|
}
|
||||||
|
header.magic = dat;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
header.boot_status_offset = dat;
|
||||||
|
|
||||||
|
valid = true;
|
||||||
|
interpret = new_string_printf("Boot status offset addr: %u", dat);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
header.config_overrides_offset = dat;
|
||||||
|
interpret = new_string_printf("Config override offset addr: %u", dat);
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
header.config_overrides_len = dat;
|
||||||
|
interpret = new_string_printf("Config override length: %u", dat);
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
header.err_memory_offset = dat;
|
||||||
|
interpret = new_string_printf("Error memory offset addr: %u", dat);
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
header.err_memory_end = dat;
|
||||||
|
interpret = new_string_printf("Error memory end ptr: %u", dat);
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
header.crc = dat;
|
||||||
|
valid = check_err_mem_header(&header, &expected_value);
|
||||||
|
if (valid) {
|
||||||
|
interpret = new_string_printf("Header CRC: 0x%08X", dat);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
interpret = new_string_printf("Invalid CRC: 0x%08X | Expected: 0x%08X", dat, expected_value);
|
||||||
|
analyze_further = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i <= 6) {
|
||||||
|
state = ENTRY_STATE_INVALID;
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're in the boot status structure */
|
||||||
|
if (i >= header.boot_status_offset && i < header.boot_status_offset + sizeof(struct safety_memory_boot_status) / 4) {
|
||||||
|
if (i == header.boot_status_offset) {
|
||||||
|
/* First entry of boot status struct */
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
interpret = new_string_printf("Boot Status Struct");
|
||||||
|
append_data(store, parent_iter, child_iter, interpret, ENTRY_STATE_BLOCK, i * 4, dat);
|
||||||
|
parent_iter = &iter;
|
||||||
|
child_iter = &iter2;
|
||||||
|
interpret = NULL;
|
||||||
|
}
|
||||||
|
switch (i - header.boot_status_offset) {
|
||||||
|
case 0:
|
||||||
|
interpret = new_string_printf("%s into bootloader", (dat ? "Boot" : "Do not boot"));
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
interpret = new_string_printf("Code %s been updated", (dat ? "has" : "hasn't"));
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
interpret = new_string_printf("Panic %s", (dat ? "occured" : "did not occur"));
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
valid = false;
|
||||||
|
interpret = new_string_printf("Value undefined!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = ENTRY_STATE_INVALID;
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= header.config_overrides_offset && i < (header.config_overrides_offset + header.config_overrides_len)) {
|
||||||
|
if (i == header.config_overrides_offset) {
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
append_data(store, parent_iter, child_iter,
|
||||||
|
new_string_printf("Config Overrides"),
|
||||||
|
ENTRY_STATE_BLOCK, i * 4, dat);
|
||||||
|
parent_iter = child_iter;
|
||||||
|
child_iter = &iter2;
|
||||||
|
}
|
||||||
|
|
||||||
|
interpret = new_string_printf("Config Overrides not yet implemented");
|
||||||
|
valid = true;
|
||||||
|
state = ENTRY_STATE_INVALID;
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= header.err_memory_offset && i < header.err_memory_end) {
|
||||||
|
if (i == header.err_memory_offset) {
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
append_data(store, parent_iter, child_iter,
|
||||||
|
new_string_printf("Error Memory (length: %d)", header.err_memory_end - header.err_memory_offset),
|
||||||
|
ENTRY_STATE_BLOCK, i * 4, dat);
|
||||||
|
parent_iter = child_iter;
|
||||||
|
child_iter = &iter2;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dat) {
|
||||||
|
case SAFETY_MEMORY_NOP_ENTRY:
|
||||||
|
valid = true;
|
||||||
|
interpret = new_string_printf("Error Memory NOP");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ((dat & 0xFFU) == 0x51U) {
|
||||||
|
valid = true;
|
||||||
|
interpret = new_string_printf("Err memory Entry");
|
||||||
|
} else
|
||||||
|
interpret = new_string_printf("Invalid error memory entry");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = ENTRY_STATE_INVALID;
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == header.err_memory_end) {
|
||||||
|
expected_value = calculate_stm_crc((uint32_t *)memory, header.err_memory_end);
|
||||||
|
if (expected_value == dat) {
|
||||||
|
interpret = new_string_printf("Valid Memory CRC");
|
||||||
|
valid = true;
|
||||||
|
} else {
|
||||||
|
interpret = new_string_printf("Invalid CRC. Expected: 0x%08X", expected_value);
|
||||||
|
}
|
||||||
|
state = ENTRY_STATE_INVALID;
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_iter = NULL;
|
||||||
|
child_iter = &iter;
|
||||||
|
print:
|
||||||
|
if (state != ENTRY_STATE_NOT_PARSED) {
|
||||||
|
state = (valid ? ENTRY_STATE_VALID : ENTRY_STATE_INVALID);
|
||||||
|
}
|
||||||
|
append_data(store, parent_iter, child_iter, interpret, state, i * 4, dat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!store)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_tree_view(GtkTreeView *tree_view)
|
||||||
|
{
|
||||||
|
GtkTreeStore *tree_store;
|
||||||
|
GtkCellRenderer *string_renderer;
|
||||||
|
GtkCellRenderer *hex_renderer;
|
||||||
|
GtkTreeViewColumn *column;
|
||||||
|
|
||||||
|
g_return_if_fail(GTK_IS_TREE_VIEW(tree_view));
|
||||||
|
|
||||||
|
tree_store = gtk_tree_store_new(MEM_VIEW_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, GDK_TYPE_RGBA);
|
||||||
|
gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store));
|
||||||
|
|
||||||
|
string_renderer = gtk_cell_renderer_text_new();
|
||||||
|
hex_renderer = err_mem_view_hex_cell_renderer_new();
|
||||||
|
|
||||||
|
column = gtk_tree_view_column_new_with_attributes("Address", hex_renderer, "hex-num", MEM_VIEW_COL_ADDRESS, "foreground-rgba", MEM_VIEW_COLOR, NULL);
|
||||||
|
gtk_tree_view_append_column(tree_view, column);
|
||||||
|
column = gtk_tree_view_column_new_with_attributes("Data", hex_renderer, "hex-num", MEM_VIEW_COL_DATA, "foreground-rgba", MEM_VIEW_COLOR, NULL);
|
||||||
|
gtk_tree_view_append_column(tree_view, column);
|
||||||
|
column = gtk_tree_view_column_new_with_attributes("Interpretation", string_renderer, "text", MEM_VIEW_COL_INTERPRETATION, "foreground-rgba", MEM_VIEW_COLOR, NULL);
|
||||||
|
gtk_tree_view_append_column(tree_view, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ptrdiff_t get_file_size(const char *filename)
|
||||||
|
{
|
||||||
|
struct stat file_stat;
|
||||||
|
int res;
|
||||||
|
ptrdiff_t size;
|
||||||
|
|
||||||
|
res = stat(filename, &file_stat);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
size = -1UL;
|
||||||
|
else
|
||||||
|
size = (ptrdiff_t)file_stat.st_size;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loads a file to memory and decodes it using base64
|
||||||
|
* @param name Filename
|
||||||
|
* @param detected_size detected Size of the ouput generated
|
||||||
|
* @return Buffer of decoded data with size \p detected_size, NULL in case of an error.
|
||||||
|
* @note Return buffer is allocated on heap. Use free() to free the memory.
|
||||||
|
*/
|
||||||
|
static char *load_file_to_memory(const char *name, size_t *detected_size)
|
||||||
|
{
|
||||||
|
ptrdiff_t size;
|
||||||
|
FILE *f;
|
||||||
|
char *ret = NULL;
|
||||||
|
size_t read_bytes;
|
||||||
|
gsize decoded_size;
|
||||||
|
|
||||||
|
size = get_file_size(name);
|
||||||
|
if (size <= 0)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
f = fopen(name, "rb");
|
||||||
|
ret = (char *)malloc(size + 1);
|
||||||
|
if (!ret)
|
||||||
|
goto exit_close_file;
|
||||||
|
|
||||||
|
read_bytes = fread(ret, 1UL, (size_t)size, f);
|
||||||
|
if (read_bytes != (size_t)size) {
|
||||||
|
free(ret);
|
||||||
|
ret = NULL;
|
||||||
|
goto exit_close_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[size] = '\0';
|
||||||
|
|
||||||
|
g_base64_decode_inplace(ret, &decoded_size);
|
||||||
|
if (decoded_size == 0) {
|
||||||
|
free(ret);
|
||||||
|
ret = NULL;
|
||||||
|
goto exit_close_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detected_size)
|
||||||
|
*detected_size = (size_t)decoded_size;
|
||||||
|
exit_close_file:
|
||||||
|
fclose(f);
|
||||||
|
exit:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT
|
||||||
|
void info_bar_close_cb(GtkInfoBar *info_bar, gpointer user_data)
|
||||||
|
{
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
gtk_info_bar_set_revealed(info_bar, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT
|
||||||
|
void open_button_clicked_cb(GtkButton *button, gpointer *user_data)
|
||||||
|
{
|
||||||
|
struct application_data *data = (struct application_data *)user_data;
|
||||||
|
(void)button;
|
||||||
|
GtkDialog *dialog;
|
||||||
|
gint res;
|
||||||
|
gchar *filename;
|
||||||
|
|
||||||
|
|
||||||
|
dialog = GTK_DIALOG(gtk_file_chooser_dialog_new("Open File", GTK_WINDOW(data->borrowed_main_window),
|
||||||
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
"Cancel", GTK_RESPONSE_CANCEL,
|
||||||
|
"Open", GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL));
|
||||||
|
res = gtk_dialog_run(dialog);
|
||||||
|
if (res == GTK_RESPONSE_ACCEPT) {
|
||||||
|
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||||
|
if (data->error_memory_data)
|
||||||
|
free((void *)data->error_memory_data);
|
||||||
|
data->error_memory_data = load_file_to_memory(filename, &data->file_size);
|
||||||
|
if (!data->error_memory_data) {
|
||||||
|
g_warning("File could not be loaded");
|
||||||
|
g_free(filename);
|
||||||
|
goto ret_destroy_dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_header_bar_set_subtitle(data->borrowed_header_bar, filename);
|
||||||
|
g_free(filename);
|
||||||
|
} else {
|
||||||
|
goto ret_destroy_dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->file_size % 4 || data->file_size == 0) {
|
||||||
|
free((void *)data->error_memory_data);
|
||||||
|
data->error_memory_data = NULL;
|
||||||
|
info_bar_show_message(data->borrowed_info_bar, GTK_MESSAGE_WARNING,
|
||||||
|
"Data must be base64 encoded and must contain full 32 bit words of data.");
|
||||||
|
goto ret_destroy_dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
show_error_memory(data->borrowed_tree_view, (const unsigned char *)data->error_memory_data, data->file_size);
|
||||||
|
|
||||||
|
ret_destroy_dialog:
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void app_activated(GApplication *app, gpointer user_data)
|
||||||
|
{
|
||||||
|
struct application_data *data = (struct application_data *)user_data;
|
||||||
|
GtkBuilder *builder;
|
||||||
|
GtkWindow *main_window;
|
||||||
|
|
||||||
|
builder = gtk_builder_new_from_resource("/gui/main");
|
||||||
|
main_window = GTK_WINDOW(gtk_builder_get_object(builder, "main-window"));
|
||||||
|
data->borrowed_main_window = GTK_WIDGET(main_window);
|
||||||
|
data->error_memory_data = NULL;
|
||||||
|
data->borrowed_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "error-mem-tree-view"));
|
||||||
|
data->borrowed_header_bar = GTK_HEADER_BAR(gtk_builder_get_object(builder, "header-bar"));
|
||||||
|
data->borrowed_info_bar = GTK_INFO_BAR(gtk_builder_get_object(builder, "info-bar"));
|
||||||
|
|
||||||
|
setup_tree_view(data->borrowed_tree_view);
|
||||||
|
|
||||||
|
gtk_builder_connect_signals(builder, data);
|
||||||
|
g_object_unref(builder);
|
||||||
|
gtk_application_add_window(GTK_APPLICATION(app), main_window);
|
||||||
|
gtk_widget_show(GTK_WIDGET(main_window));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int start_gui(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
GtkApplication *g_app;
|
||||||
|
static struct application_data data;
|
||||||
|
|
||||||
|
/* Create a new G application which will start a completely new process for each call to the program (NON_UNIQUE) instead
|
||||||
|
* of creating an additional window in the same process
|
||||||
|
*/
|
||||||
|
g_app = gtk_application_new("de.shimatta.reflow.error-mem-viewer", G_APPLICATION_NON_UNIQUE);
|
||||||
|
g_signal_connect(g_app, "activate", G_CALLBACK(app_activated), &data);
|
||||||
|
ret = g_application_run(G_APPLICATION(g_app), argc, argv);
|
||||||
|
g_object_unref(g_app);
|
||||||
|
if (data.error_memory_data)
|
||||||
|
free((void *)data.error_memory_data);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return start_gui(argc, argv);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user