-
Notifications
You must be signed in to change notification settings - Fork 89
rpierrorlog: Add rpierrorlog to utils to manage firmware error logging #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
martamomotko
wants to merge
1
commit into
raspberrypi:master
Choose a base branch
from
martamomotko:add-rpierrorlog-util
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| cmake_minimum_required(VERSION 3.10...3.27) | ||
| include(GNUInstallDirs) | ||
|
|
||
| set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") | ||
|
|
||
| project(rpierrorlog) | ||
|
|
||
| option(BUILD_SHARED_LIBS "Build using shared libraries" ON) | ||
|
|
||
| add_library(rpierrorlog rpierrorlog.c) | ||
| target_sources(rpierrorlog PUBLIC rpierrorlog.h) | ||
| set_target_properties(rpierrorlog PROPERTIES PUBLIC_HEADER rpierrorlog.h) | ||
| set_target_properties(rpierrorlog PROPERTIES SOVERSION 0) | ||
|
|
||
| add_executable(rpi-error-log main.c) | ||
| target_link_libraries(rpi-error-log rpierrorlog) | ||
|
|
||
| install(TARGETS rpi-error-log RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | ||
| install(TARGETS rpierrorlog | ||
| ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||
| PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
|
|
||
| # rpierrorlog | ||
|
|
||
| The Raspberry Pi Error Log service is a mailbox based API for reading | ||
| and clearing error logs stored in SPI EEPROM. These logs are written | ||
| by the firmware when a fatal failure occurs and persist across reboots. | ||
|
|
||
| Although this service can be used via raw vcmailbox commands the | ||
| recommended API is either the command line rpi-error-log application | ||
| or the librpierrorlog.so shared library. | ||
|
|
||
| **Build Instructions** | ||
| Install prerequisites with "sudo apt install cmake" - you need at least version 3.10. | ||
|
|
||
| - *mkdir build* | ||
| - *cd build* | ||
| - *cmake ..* | ||
| - *make* | ||
| - *sudo make install* | ||
|
|
||
| **Usage** | ||
|
|
||
| * rpi-error-log (Shows usage information) | ||
| * rpi-error-log get (Read and print all EEPROM error log entries - Pi 4 and Pi 5 family boards only) | ||
| * rpi-error-log clear (Clear the EEPROM error log and verify it is empty - Pi 5 family boards only) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include "rpierrorlog.h" | ||
|
|
||
| static void usage(const char *progname) | ||
| { | ||
| fprintf(stderr, | ||
| "Usage: %s <command>\n" | ||
| "\n" | ||
| "Commands:\n" | ||
| " get Read and print all EEPROM error log entries\n" | ||
| " (Pi 4 and Pi 5 family boards only)\n" | ||
| " clear Clear the EEPROM error log and verify it is empty\n" | ||
| " (Pi 5 family boards only)\n", | ||
| progname); | ||
| } | ||
|
|
||
| static int cmd_get(void) | ||
| { | ||
| rpi_error_log_t entries[RPI_ERROR_LOG_MAX_SIZE / RPI_ERROR_LOG_ENTRY_SIZE]; | ||
| size_t count; | ||
|
|
||
| if (rpi_error_log_get(entries, sizeof(entries) / sizeof(entries[0]), &count) < 0) { | ||
| fprintf(stderr, "Failed to read error log\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| if (count == 0) { | ||
| printf("No error log entries\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| for (size_t i = 0; i < count; i++) | ||
| printf("Entry %zu: 0x%08x - %s\n", i, entries[i].error_code, rpi_error_log_strerror(entries[i].error_code)); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int cmd_clear(void) | ||
| { | ||
| rpi_error_log_t entries[RPI_ERROR_LOG_MAX_SIZE / RPI_ERROR_LOG_ENTRY_SIZE]; | ||
| size_t count; | ||
|
|
||
| if (rpi_error_log_clear() < 0) { | ||
| fprintf(stderr, "Failed to clear error log\n"); | ||
| return -1; | ||
| } | ||
| printf("Error log cleared\n"); | ||
|
|
||
| if (rpi_error_log_get(entries, sizeof(entries) / sizeof(entries[0]), &count) < 0) { | ||
| fprintf(stderr, "Verification read failed\n"); | ||
| return -1; | ||
| } | ||
| if (count != 0) { | ||
| fprintf(stderr, "Verification failed: %zu entries still present after clear\n", count); | ||
| return -1; | ||
| } | ||
| printf("Verification OK: error log is empty\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| int main(int argc, char *argv[]) | ||
| { | ||
| int rc = -1; | ||
|
|
||
| if (argc != 2) { | ||
| usage(argv[0]); | ||
| return 1; | ||
| } | ||
|
|
||
| if (strcmp(argv[1], "get") == 0) { | ||
| rc = cmd_get(); | ||
| if (rc < 0) | ||
| goto error; | ||
| return 0; | ||
| } | ||
|
|
||
| if (strcmp(argv[1], "clear") == 0) { | ||
| rc = cmd_clear(); | ||
| if (rc < 0) | ||
| goto error; | ||
| return 0; | ||
| } | ||
|
|
||
| usage(argv[0]); | ||
| return 1; | ||
|
|
||
| error: | ||
| fprintf(stderr, "Command '%s' failed\n", argv[1]); | ||
| return rc; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| #include <stdio.h> | ||
| #include <stdint.h> | ||
| #include <string.h> | ||
| #include <fcntl.h> | ||
| #include <unistd.h> | ||
| #include <sys/ioctl.h> | ||
| #include <errno.h> | ||
| #include "rpierrorlog.h" | ||
|
|
||
| #define DEVICE_FILE_NAME "/dev/vcio" | ||
| #define MAJOR_NUM 100 | ||
| #define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) | ||
|
|
||
| #if 0 | ||
| #define LOG_DEBUG(...) do { \ | ||
| fprintf(stderr, __VA_ARGS__); \ | ||
| } while (0) | ||
| #else | ||
| #define LOG_DEBUG(...) do { } while (0) | ||
| #endif | ||
|
|
||
| /* VideoCore mailbox error flag */ | ||
| #define VC_MAILBOX_ERROR 0x80000000u | ||
|
|
||
| /* Error log mailbox tags */ | ||
| #define TAG_GET_EEPROM_ERROR_LOGS 0x0003009au /* Read error log entries from EEPROM */ | ||
| #define TAG_SET_EEPROM_ERROR_LOGS 0x0003809au /* Clear error log in EEPROM */ | ||
|
|
||
| /* Common header for all firmware mailbox messages */ | ||
| struct firmware_msg_header { | ||
| uint32_t buf_size; | ||
| uint32_t code; | ||
| uint32_t tag; | ||
| uint32_t tag_buf_size; | ||
| uint32_t tag_req_resp_size; | ||
| }; | ||
|
|
||
| /* Mailbox message for TAG_GET_EEPROM_ERROR_LOGS. | ||
| * | ||
| * Request: reserved (ignored, 0) + length (max bytes to receive) | ||
| * Response: status (VC_MAILBOX_ERROR on failure) + length (bytes written) + data | ||
| */ | ||
| struct firmware_error_log_msg { | ||
| struct firmware_msg_header hdr; | ||
| union { | ||
| struct { | ||
| uint32_t reserved; | ||
| uint32_t length; | ||
| } req; | ||
| struct { | ||
| uint32_t status; | ||
| uint32_t length; | ||
| uint8_t data[RPI_ERROR_LOG_MAX_SIZE]; | ||
| } resp; | ||
| }; | ||
| uint32_t end_tag; | ||
| }; | ||
|
|
||
| /* Mailbox message for TAG_SET_EEPROM_ERROR_LOGS. | ||
| * | ||
| * Request: reserved[2] (ignored) | ||
| * Response: status (VC_MAILBOX_ERROR on failure) + reserved | ||
| */ | ||
| struct firmware_error_log_clear_msg { | ||
| struct firmware_msg_header hdr; | ||
| union { | ||
| struct { | ||
| uint32_t reserved[2]; | ||
| } req; | ||
| struct { | ||
| uint32_t status; | ||
| uint32_t reserved; | ||
| } resp; | ||
| }; | ||
| uint32_t end_tag; | ||
| }; | ||
|
|
||
| static int mbox_open(void) | ||
| { | ||
| int fd = open(DEVICE_FILE_NAME, O_RDWR); | ||
| if (fd < 0) | ||
| fprintf(stderr, "Failed to open %s: %s\n", DEVICE_FILE_NAME, strerror(errno)); | ||
| return fd; | ||
| } | ||
|
|
||
| static void mbox_close(int fd) | ||
| { | ||
| close(fd); | ||
| } | ||
|
|
||
| static int mbox_property(int fd, void *msg) | ||
| { | ||
| struct firmware_msg_header *hdr = (struct firmware_msg_header *)msg; | ||
| int rc = ioctl(fd, IOCTL_MBOX_PROPERTY, msg); | ||
| if (rc < 0) | ||
| fprintf(stderr, "ioctl_mbox_property failed: %d\n", rc); | ||
|
|
||
| LOG_DEBUG("hdr.code=0x%08x tag=0x%08x tag_buf_size=%u tag_req_resp_size=0x%08x\n", | ||
| hdr->code, hdr->tag, hdr->tag_buf_size, hdr->tag_req_resp_size); | ||
|
|
||
| if (!(hdr->code & VC_MAILBOX_ERROR) || | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated rpi-fw-crypto to make this less confusing because bit31 doesn't mean ERROR error. It means tag completed. |
||
| !(hdr->tag_req_resp_size & VC_MAILBOX_ERROR)) | ||
| return -1; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| const char *rpi_error_log_strerror(uint32_t error_code) | ||
| { | ||
| switch (error_code) { | ||
| case 0x03: return "Generic boot failure"; | ||
| case 0x04: return "Firmware (start*.elf) not found"; | ||
| case 0x07: return "Kernel or device-tree not found or is not compatible"; | ||
| case 0x08: return "SDRAM failure"; | ||
| case 0x09: return "SDRAM mismatch"; | ||
| case 0x0a: return "Halting"; | ||
| case 0x11: return "Operation requires USB high current limit"; | ||
| case 0x12: return "SD card overcurrent"; | ||
| case 0x21: return "Partition is not FAT"; | ||
| case 0x22: return "Failed to read from partition"; | ||
| case 0x23: return "Extended partition not FAT"; | ||
| case 0x24: return "File signature or hash mismatch"; | ||
| case 0x31: return "SPI EEPROM error"; | ||
| case 0x32: return "EEPROM is write protected"; | ||
| case 0x33: return "I2C error"; | ||
| case 0x34: return "Configuration error"; | ||
| case 0x43: return "RP1 not found"; | ||
| case 0x44: return "Unsupported board type"; | ||
| case 0x45: return "Fatal firmware error"; | ||
| case 0x46: return "Power failure type A"; | ||
| case 0x47: return "Power failure type B"; | ||
| default: return "Unknown"; | ||
| } | ||
| } | ||
|
|
||
| int rpi_error_log_get(rpi_error_log_t *entries, size_t max_entries, size_t *out_count) | ||
| { | ||
| struct firmware_error_log_msg msg = {0}; | ||
| size_t num_entries, i; | ||
| int mb, rc; | ||
|
|
||
| if (!entries || !out_count || max_entries == 0) | ||
| return -1; | ||
|
|
||
| mb = mbox_open(); | ||
| if (mb < 0) | ||
| return -1; | ||
|
|
||
| msg.hdr.buf_size = sizeof(msg); | ||
| msg.hdr.tag = TAG_GET_EEPROM_ERROR_LOGS; | ||
| msg.hdr.tag_buf_size = sizeof(msg.resp); | ||
| msg.req.reserved = 0; | ||
| msg.req.length = RPI_ERROR_LOG_MAX_SIZE; | ||
|
|
||
| rc = mbox_property(mb, &msg); | ||
| mbox_close(mb); | ||
|
|
||
| if (rc < 0) | ||
| return -1; | ||
|
|
||
| if (msg.resp.status & VC_MAILBOX_ERROR) { | ||
| /* Firmware error means no error log file exists in EEPROM */ | ||
| *out_count = 0; | ||
| return 0; | ||
| } | ||
|
|
||
| num_entries = msg.resp.length / RPI_ERROR_LOG_ENTRY_SIZE; | ||
| if (num_entries > max_entries) | ||
| num_entries = max_entries; | ||
|
|
||
| for (i = 0; i < num_entries; i++) { | ||
| uint32_t ec, ec_inv; | ||
| size_t offset = i * RPI_ERROR_LOG_ENTRY_SIZE; | ||
|
|
||
| memcpy(&ec, msg.resp.data + offset, sizeof(ec)); | ||
| memcpy(&ec_inv, msg.resp.data + offset + 4, sizeof(ec_inv)); | ||
|
|
||
| LOG_DEBUG("entry[%zu] ec=0x%08x ec_inv=0x%08x\n", i, ec, ec_inv); | ||
|
|
||
| if (ec == 0xffffffffu) | ||
| break; | ||
|
|
||
| if (ec_inv != (~ec & 0xffffffffu)) { | ||
| fprintf(stderr, "Entry %zu checksum invalid\n", i); | ||
| return -1; | ||
| } | ||
|
|
||
| entries[i].error_code = ec; | ||
| } | ||
|
|
||
| *out_count = i; | ||
| return 0; | ||
| } | ||
|
|
||
| int rpi_error_log_clear(void) | ||
| { | ||
| struct firmware_error_log_clear_msg msg = {0}; | ||
| int mb, rc; | ||
|
|
||
| mb = mbox_open(); | ||
| if (mb < 0) | ||
| return -1; | ||
|
|
||
| msg.hdr.buf_size = sizeof(msg); | ||
| msg.hdr.tag = TAG_SET_EEPROM_ERROR_LOGS; | ||
| msg.hdr.tag_buf_size = sizeof(msg.resp); | ||
|
|
||
| rc = mbox_property(mb, &msg); | ||
| mbox_close(mb); | ||
|
|
||
| if (rc < 0) | ||
| return -1; | ||
|
|
||
| if (msg.resp.status & VC_MAILBOX_ERROR) { | ||
| fprintf(stderr, "Firmware returned error for SET_EEPROM_ERROR_LOGS\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please can you also update the top level readme with a suitable description