diff --git a/common.lua b/common.lua index ad4867b..dccc561 100644 --- a/common.lua +++ b/common.lua @@ -8,3 +8,12 @@ function convert_to_uf2(target) os.execv("uf2conv", { firmware .. ".strip", "-b0x10000000", "-f0x6CE29E60", "-co", firmware .. ".slot2.uf2" }) os.execv("uf2conv", { firmware .. ".strip", "-b0x20000000", "-f0x6CE29E60", "-co", firmware .. ".slot3.uf2" }) end + +function convert_to_vmem(target) + local firmware = target:targetfile() + os.execv("riscv32-unknown-elf-objcopy", { firmware, "-O", "binary" , firmware .. ".bin" }) + os.execv("srec_cat", { firmware .. ".bin", "-binary", + "-offset" ,"0x0000", "-byte-swap", "4", + "-o", firmware .. ".vmem", "-vmem" }) +end + diff --git a/examples/heartbleed/cheriot/heartbleed.cc b/examples/heartbleed/cheriot/heartbleed.cc new file mode 100644 index 0000000..1681818 --- /dev/null +++ b/examples/heartbleed/cheriot/heartbleed.cc @@ -0,0 +1,356 @@ +// Copyright lowRISC Contributors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +#include "../../../libraries/lcd.hh" +#include + +#include "../common.h" + +using namespace CHERI; +using namespace sonata::lcd; +SonataLcd *lcd = nullptr; + +constexpr bool DebugDemo = true; +constexpr uint32_t LengthScrollMillis = 150u; +constexpr Color BackgroundColor = Color::Black; +constexpr Color ForegroundColor = Color::White; + +using Debug = ConditionalDebug; +using JoystickDirection = SonataGpioBoard::JoystickDirection; +using JoystickValue = SonataGpioBoard::JoystickValue; + +/** + * @brief Busy waits for a given amount of time, constantly polling for any + * joystick input and recording it to avoid inputs being eaten between + * frames. This will only avoid inputs being eaten if the joystick did not + * move on the previous invocation of this function. This produces behaviour + * where holding the joystick ends when letting go of the joystick, but a + * single small tap between frames will be recorded. + * + * @param milliseconds The time to wait for in milliseconds. + * @param gpio The Sonata GPIO driver to use for I/O operations. + * @param onlyFirst True if this should only record the first joystick input, + * False if it should record all (OR them together). + * @param consecutive True if this should avoid eating inputs on consecutive + * inputs in the same direction, False if it should only poll for the first + * movement in a direction. + * @return The joystick state that was read. + */ +JoystickValue wait_with_input(uint32_t milliseconds, + volatile SonataGpioBoard *gpio, + bool onlyFirst, + bool consecutive) +{ + static JoystickDirection prevDirection = + static_cast(0x0); + + const uint32_t CyclesPerMillisecond = CPU_TIMER_HZ / 1000; + const uint32_t Cycles = milliseconds * CyclesPerMillisecond; + const uint64_t Start = rdcycle64(); + uint64_t end = Start + Cycles; + uint64_t current = Start; + JoystickDirection joystickInput = static_cast(0x0); + while (end > current) + { + if (onlyFirst && !joystickInput) + { + joystickInput = static_cast( + gpio->input & SonataGpioBoard::Inputs::Joystick); + } + else if (!onlyFirst) + { + joystickInput = static_cast( + (gpio->input & SonataGpioBoard::Inputs::Joystick) | + static_cast(joystickInput)); + } + current = rdcycle64(); + } + + // If this is the first invocation in which the joystick was held in this + // direction (i.e. a 'tap' or the start of a hold), or the `consecutive` + // setting is set, use the polled input to avoid inputs being eaten. + // If the `consecutive` setting is False and we previously moved in this + // direction, use only the GPIO read at the end of waiting. + JoystickDirection retDirection; + if (!consecutive && (static_cast(prevDirection) & + static_cast(joystickInput))) + { + retDirection = static_cast( + gpio->input & SonataGpioBoard::Inputs::Joystick); + } + else + { + retDirection = joystickInput; + } + prevDirection = joystickInput; + return {retDirection}; +}; + +/** + * @brief Waits for `LengthScrollMillis` while polling constantly for user + * input on the joystick. Uses joystick up/down movements to control the + * size of the request length, and detects pressed to submit the "heartbeat" + * request. + * + * @param gpio The Sonata GPIO driver to use for I/O operations + * @param current A param/outparam storing the current response length, which + * will be updated to store the new response length if it changes. + * @return Whether to submit the response or not (true = submit). + */ +bool length_joystick_control(volatile SonataGpioBoard *gpio, size_t *current) +{ + constexpr JoystickDirection IncreaseDirection = + static_cast(JoystickDirection::Up | + JoystickDirection::Right); + constexpr JoystickDirection DecreaseDirection = + static_cast(JoystickDirection::Down | + JoystickDirection::Left); + constexpr size_t SizeLimit = 256; + + auto joystickInput = wait_with_input(LengthScrollMillis, gpio, true, false); + if (joystickInput.is_direction_pressed(IncreaseDirection)) + { + if (!joystickInput.is_direction_pressed(DecreaseDirection)) + { + *current = (*current >= SizeLimit) ? SizeLimit : (*current + 1); + } + } + else if (joystickInput.is_direction_pressed(DecreaseDirection)) + { + *current = (*current <= 0) ? 0 : (*current - 1); + } + else + { + return joystickInput.is_pressed(); + } + return false; +} + +/** + * @brief Display the initial demo information to the LCD, including controls + * and initial heartbeat request information. + * + * @param lcd The Sonata LCD driver to use. + * @param bufferMessage The (not null-terminated) message that will be sent. + * @param actual_req_len The genuine length of the `bufferMessage`. This MUST + * match, or is undefined behaviour. + */ +void initial_lcd_write(SonataLcd *lcd) +{ + lcd->draw_str({5, 5}, + "Move Joystick to Change Length.", + BackgroundColor, + ForegroundColor, + Font::M5x7_16pt); + lcd->draw_str({5, 15}, + "Press Joystick to Send.", + BackgroundColor, + ForegroundColor, + Font::M5x7_16pt); + lcd->draw_str({5, 30}, + "Request a larger buffer ", + BackgroundColor, + ForegroundColor, + Font::M5x7_16pt); + lcd->draw_str({5, 40}, + "Suggested Length: ", + BackgroundColor, + ForegroundColor, + Font::M5x7_16pt); +} + +/** + * @brief Display the request length integer to the LCD. + * + * @param lcd The Sonata LCD Driver + * @param gpio The Sonata GPIO driver to use for I/O operations + * @param request_length The request length to display + */ +void draw_request_length(SonataLcd *lcd, + volatile SonataGpioBoard *gpio, + size_t request_length) +{ + constexpr size_t ReqLenStrLen = 15u; + char req_len_s[ReqLenStrLen]; + size_t_to_str_base10(req_len_s, request_length); + lcd->draw_str( + {110, 40}, req_len_s, BackgroundColor, ForegroundColor, Font::M5x7_16pt); +} + +/** + * @brief This function contains the control logic loop to fetch joystick + * input related to the request length until the user presses the joystick + * to submit the request. + * + * @param lcd The Sonata LCD Driver + * @param gpio The Sonata GPIO driver to use for I/O operations + * @param request_length An out parameter containing the initial request + * length, and to which the final request length will be written. + */ +void get_request_length(SonataLcd *lcd, + volatile SonataGpioBoard *gpio, + size_t *request_length) +{ + // Initial draw + draw_request_length(lcd, gpio, *request_length); + + // Get user input + Debug::log("Waiting for user input on the joystick..."); + bool inputSubmitted = false; + while (!inputSubmitted) + { + size_t prev_length = *request_length; + inputSubmitted = length_joystick_control(gpio, request_length); + if (*request_length == prev_length) + { + continue; // Only re-draw when changed + } + lcd->draw_str({110, 40}, + " ", + BackgroundColor, + ForegroundColor, + Font::M5x7_16pt); + draw_request_length(lcd, gpio, *request_length); + } + + Debug::log("Heartbeat submitted with length {}", (int)(*request_length)); +} + +/** + * @brief This function mocks the network and show the package on the lcd + * instead. + * + * @param lcd A handle to Sonata's LCD + * @param package The package to be sent. + * @param len The length of the package . + */ +void network_send(void *handle, const char *package, size_t len) +{ + constexpr uint32_t CharsPerLine = 29; + SonataLcd *lcd = (SonataLcd *)handle; + + size_t w_border = 2; + lcd->fill_rect({w_border, 50, 160 - w_border, 128}, Color::Grey); + + // Break the result message into several lines if it is too long to fit on + // one line. + char line_content[CharsPerLine + 1]; + const char *cursor = package; + size_t line_length = 0u; + size_t line_num = 0u; + while (len-- != 0) + { + if (*cursor == 0) + { + line_content[line_length++] = '`'; + } + else if (isprint((int)(*cursor)) == 0) + { + line_content[line_length++] = '%'; + } + else + { + line_content[line_length++] = *cursor; + } + cursor++; + if (line_length == CharsPerLine) + { + line_content[line_length] = '\0'; + lcd->draw_str({5, 55 + 10 * line_num}, + line_content, + Color::Grey, + Color::Black, + Font::M5x7_16pt); + line_length = 0; + line_num++; + } + } + // Write the final line containing the remainder of the message. + if (line_length) + { + line_content[line_length] = '\0'; + lcd->draw_str({5, 55 + 10 * line_num}, + line_content, + Color::Grey, + Color::Black, + Font::M5x7_16pt); + } +} + +/** + * @brief Handles any CHERI Capability Violation errors. If the error was a + * Bounds or Tag violation it assumes it is because of the incorrect memory + * access in SnakeGame::check_if_colliding and therefore it recovers the program + * and ends the game. Otherwise, this force unwinds and ends the program. + */ +extern "C" ErrorRecoveryBehaviour +compartment_error_handler(ErrorState *frame, size_t mcause, size_t mtval) +{ + lcd->fill_rect({0, 0, 160, 128}, Color::Blue); + lcd->draw_str( + {5, 30}, "Oops ;-(", Color::Blue, Color::White, Font::LucidaConsole_12pt); + lcd->draw_str({30, 80}, + "Protected by", + Color::Blue, + Color::White, + Font::LucidaConsole_10pt); + lcd->draw_str( + {50, 95}, "CHERIoT", Color::Blue, Color::White, Font::LucidaConsole_10pt); + + Debug::log( + "Unexpected CHERI Capability violation encountered. Stopping..."); + while (1) + { + asm("wfi"); + } + return ErrorRecoveryBehaviour::ForceUnwind; +} + +[[noreturn]] void __cheri_compartment("heartbleed") entry() +{ + // Initialise the LCD driver and calculate display information + lcd = new SonataLcd(); + Size displaySize = lcd->resolution(); + Point centre = {displaySize.width / 2, displaySize.height / 2}; + lcd->clean(BackgroundColor); + size_t w_border = 2; + lcd->fill_rect({w_border, 50, 160 - w_border, 128}, Color::Grey); + + // Initialise GPIO driver to interact with the Joystick + auto gpio = MMIO_CAPABILITY(SonataGpioBoard, gpio_board); + + size_t req_len = 8; + while (true) + { + // We allocate a big chunck of memory to temporary store a json file + // with sensitive information. Then we free the buffer without cleaning + // the content. + const size_t DbSize = 128; + char *ptr = (char *)malloc(DbSize); + read_file("clients.db", ptr, DbSize); + free(ptr); + + initial_lcd_write(lcd); + + // Wait for the request. + get_request_length(lcd, gpio, &req_len); + + const char *result = + run_query("SELECT name FROM animal WHERE can_fly=yes LIMIT 1"); + + // We send back the response to the request without checking that the + // requested length exeeds the needed size, which can leek information. + heartbleed(lcd, result, req_len); + + free((void *)result); + } + + // Driver cleanup + delete lcd; +} diff --git a/examples/heartbleed/cheriot/xmake.lua b/examples/heartbleed/cheriot/xmake.lua new file mode 100644 index 0000000..f513b5a --- /dev/null +++ b/examples/heartbleed/cheriot/xmake.lua @@ -0,0 +1,24 @@ +-- Copyright lowRISC Contributors. +-- SPDX-License-Identifier: Apache-2.0 + +-- Compartments used for the automotive demo firmware +compartment("heartbleed") + add_deps("lcd", "debug", "string") + add_files("heartbleed.cc", "../common.c") + +-- CHERIoT version of Heartbleed Demo Firmware +firmware("heartbleed_cheriot") + add_deps("freestanding", "heartbleed") + on_load(function(target) + target:values_set("board", "$(board)") + target:values_set("threads", { + { + compartment = "heartbleed", + priority = 2, + entry_point = "entry", + stack_size = 0x800, + trusted_stack_frames = 2 + } + }, {expand = false}) + end) + after_link(convert_to_uf2) diff --git a/examples/heartbleed/common.c b/examples/heartbleed/common.c new file mode 100644 index 0000000..b62c732 --- /dev/null +++ b/examples/heartbleed/common.c @@ -0,0 +1,85 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "common.h" + +#include + +#if __has_builtin(__builtin_launder) +# define LAUNDER(_p_) __builtin_launder(_p_) +#else +# define LAUNDER(_p_) (_p_) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus + + // clang-format off +static const char *json = + "[{user: John Von Neumann, pin: 123498," + "user: James Clerk Maxwell, pin: 488758}," + "{log: John transfered 100 Bitcoins to James}" + "{log: James Withdrew 100 Bitcoins. }]"; + // clang-format on + + // We represent the heartbeat message as a small array of incoming bytes, + // and initialise with its actual size. + static const char *QueryResponse = "Sparrow"; + + // We don't have filesystem so we mock it. + void read_file(const char *filename, char *buffer, size_t buffer_size) + { + strncpy(buffer, json, buffer_size); + } + + // We don't have a query engine so we mock it. + char *run_query(const char *query) + { + char *buff = (char *)malloc(strlen(QueryResponse) + 1); + + strcpy(buff, QueryResponse); + return buff; + } + + void heartbleed(void *handle, const char *buffer, size_t len) + { + const size_t headerLen = 7u; + char package[headerLen + len + 1]; + memcpy(package, "{Resp: ", headerLen); + memcpy(&package[headerLen], buffer, len); + package[headerLen + len] = '\0'; + network_send(handle, package, sizeof(package)); + } + + void size_t_to_str_base10(char *buffer, size_t num) + { + // Parse the digits using repeated remainders mod 10 + ptrdiff_t endIdx = 0; + if (num == 0) + { + buffer[endIdx++] = '0'; + } + while (num != 0) + { + int remainder = num % 10; + buffer[endIdx++] = '0' + remainder; + num /= 10; + } + buffer[endIdx--] = '\0'; + + // Reverse the generated string + ptrdiff_t startIdx = 0; + while (startIdx < endIdx) + { + char swap = buffer[startIdx]; + buffer[startIdx++] = buffer[endIdx]; + buffer[endIdx--] = swap; + } + } + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/examples/heartbleed/common.h b/examples/heartbleed/common.h new file mode 100644 index 0000000..b67449e --- /dev/null +++ b/examples/heartbleed/common.h @@ -0,0 +1,66 @@ +// Copyright lowRISC Contributors. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef _HEARTBLEED_COMMON_H_ +#define _HEARTBLEED_COMMON_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus + + extern void network_send(void *handle, const char *package, size_t len); + + /** + * @brief Run a query and allocate a buffer with the response, the buffer + * should be deallocated by the caller. + * + * @param query The query to be executed. + * @return A heap-allocated copy of the message. Simulates a response + * packet. + */ + char *run_query(const char *query); + + /** + * @brief Format the response package, the buffer is copied to the response + * package based on the length, if larger than the buffer, sensitive + * information can be leaked throught the network. + * + * This function requires that the caller implements the function + * `network_send`. + * + * @param handle A pointer to a handle that will be forward to the + * network_send. + * @param buffer The buffer to be sent. + * @param buffer_size The size of the buffer. + * @return A heap-allocated copy of the message. Simulates a response + * packet. + */ + void heartbleed(void *handle, const char *buffer, size_t len); + + /** + * @brief Converts a given size_t to an equivalent string representing its + * unsigned base 10 representation in the given buffer. + * + * @param buffer The buffer/string to write the converted number to. Will + * terminate at the end of the string. + * @param num The size_t number to convert + */ + void size_t_to_str_base10(char *buffer, size_t num); + + /** + * @brief Reads a file content in the given buffer. + * + * @param filename The name of the file. + * @param buffer The buffer/string to write the file content. + * @param buffer_size The of the buffer. + */ + void read_file(const char *filename, char *buffer, size_t buffer_size); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif // _HEARTBLEED_COMMON_H_ diff --git a/examples/heartbleed/legacy/heartbleed.c b/examples/heartbleed/legacy/heartbleed.c new file mode 100644 index 0000000..cbf549e --- /dev/null +++ b/examples/heartbleed/legacy/heartbleed.c @@ -0,0 +1,335 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" +#include "heartbleed.h" +#include "lcd.h" + +#define DEBUG_DEMO true +#define LENGTH_SCROLL_MILLIS 150u +#define BACKGROUND_COLOR BGRColorBlack +#define FOREGROUND_COLOR BGRColorWhite + +// Define our own GPIO_IN_DBNC as the version from `sonata-system` uses void +// pointer arithmetic, which clang-tidy forbids. +#define GPIO_IN_DBNC_AM GPIO_FROM_BASE_ADDR((GPIO_BASE + GPIO_IN_DBNC_REG)) + +/** + * @brief Busy waits for a given amount of time, constantly polling for any + * joystick input and recording it to avoid inputs being eaten between + * frames. This will only avoid inputs being eaten if the joystick did not + * move on the previous invocation of this function. This produces behaviour + * where holding the joystick ends when letting go of the joystick, but a + * single small tap between frames will be recorded. + * + * @param milliseconds The time to wait for in milliseconds. + * @param onlyFirst True if this should only record the first joystick input, + * False if it should record all (OR them together). + * @param consecutive True if this should avoid eating inputs on consecutive + * inputs in the same direction, False if it should only poll for the first + * movement in a direction. + * @return The joystick state that was read. + */ +uint8_t wait_with_input(uint32_t milliseconds, bool onlyFirst, bool consecutive) +{ + static uint8_t prevDirection = 0x0; + + const uint32_t CyclesPerMillisecond = CPU_TIMER_HZ / 1000; + const uint32_t Cycles = milliseconds * CyclesPerMillisecond; + const uint64_t Start = rdcycle64(); + uint64_t end = Start + Cycles; + uint64_t current = Start; + uint8_t joystickInput = 0x0; + while (end > current) + { + if (onlyFirst && !joystickInput) + { + joystickInput = read_joystick(); + } + else if (!onlyFirst) + { + joystickInput |= read_joystick(); + } + current = rdcycle64(); + } + + // If this is the first invocation in which the joystick was held in this + // direction (i.e. a 'tap' or the start of a hold), or the `consecutive` + // setting is set, use the polled input to avoid inputs being eaten. + // If the `consecutive` setting is False and we previously moved in this + // direction, use only the GPIO read at the end of waiting. + uint8_t retDirection; + if (!consecutive && (prevDirection & joystickInput)) + { + retDirection = read_joystick(); + } + else + { + retDirection = joystickInput; + } + prevDirection = joystickInput; + return retDirection; +}; + +/** + * @brief Waits for `LENGTH_SCROLL_MILLIS` while polling constantly for user + * input on the joystick. Uses joystick up/down movements to control the + * size of the request length, and detects pressed to submit the "heartbeat" + * request. + * + * @param gpio The Sonata GPIO driver to use for I/O operations + * @param current A param/outparam storing the current response length, which + * will be updated to store the new response length if it changes. + * @return Whether to submit the response or not (true = submit). + */ +bool length_joystick_control(size_t *current) +{ + const enum JoystickDir IncreaseDirection = JoystickUp | JoystickRight; + const enum JoystickDir DecreaseDirection = JoystickDown | JoystickLeft; + const size_t SizeLimit = 256; + + uint8_t joystickInput = wait_with_input(LENGTH_SCROLL_MILLIS, true, false); + if (joystick_in_direction(joystickInput, IncreaseDirection)) + { + if (!joystick_in_direction(joystickInput, DecreaseDirection)) + { + *current = (*current >= SizeLimit) ? SizeLimit : (*current + 1); + } + } + else if (joystick_in_direction(joystickInput, DecreaseDirection)) + { + *current = (*current <= 0) ? 0 : (*current - 1); + } + else + { + return joystick_is_pressed(joystickInput); + } + return false; +} + +/** + * @brief Display the initial demo information to the LCD, including controls + * and initial heartbeat request information. + * + * @param lcd A handle to Sonata's LCD. + * @param bufferMessage The (not null-terminated) message that will be sent. + * @param actual_req_len The genuine length of the `bufferMessage`. This MUST + * match, or is undefined behaviour. + */ +void initial_lcd_write(St7735Context *lcd) +{ + lcd_draw_str(lcd, + 5, + 5, + LcdFontM5x7_16pt, + "Move Joystick to Change Length.", + BACKGROUND_COLOR, + FOREGROUND_COLOR); + lcd_draw_str(lcd, + 5, + 15, + LcdFontM5x7_16pt, + "Press Joystick to Send.", + BACKGROUND_COLOR, + FOREGROUND_COLOR); + lcd_draw_str(lcd, + 5, + 30, + LcdFontM5x7_16pt, + "Request a larger buffer", + BACKGROUND_COLOR, + FOREGROUND_COLOR); + lcd_draw_str(lcd, + 5, + 40, + LcdFontM5x7_16pt, + "Suggested Length: ", + BACKGROUND_COLOR, + FOREGROUND_COLOR); +} + +/** + * @brief Display the request length integer to the LCD. + * + * @param lcd A handle to Sonata's LCD + * @param request_length The request length to write + */ +void draw_request_length(St7735Context *lcd, size_t request_length) +{ + const size_t ReqLenStrLen = 15u; + char req_len_s[ReqLenStrLen]; + size_t_to_str_base10(req_len_s, request_length); + lcd_draw_str(lcd, + 110, + 40, + LcdFontM5x7_16pt, + req_len_s, + BACKGROUND_COLOR, + FOREGROUND_COLOR); +} + +/** + * @brief This function contains the control logic loop to fetch joystick + * input related to the request length until the user presses the joystick + * to submit the request. + * + * @param lcd A handle to Sonata's LCD + * @param request_length An out parameter containing the initial request + * length, and to which the final request length will be written. + */ +void get_request_length(St7735Context *lcd, size_t *request_length) +{ + // Initial draw + draw_request_length(lcd, *request_length); + + // Get user input + DEBUG_LOG("Waiting for user input on the joystick...\n"); + bool inputSubmitted = false; + while (!inputSubmitted) + { + size_t prev_length = *request_length; + inputSubmitted = length_joystick_control(request_length); + if (*request_length == prev_length) + { + continue; // Only re-draw when changed + } + lcd_draw_str(lcd, + 110, + 40, + LcdFontM5x7_16pt, + " ", + BACKGROUND_COLOR, + FOREGROUND_COLOR); + draw_request_length(lcd, *request_length); + } + + DEBUG_LOG("Heartbeat submitted with length %d\n", (int)(*request_length)); +} + +/** + * @brief This function mocks the network and show the package on the lcd + * instead. + * + * @param lcd A handle to Sonata's LCD + * @param package The package to be sent. + * @param len The length of the package . + */ +void network_send(void *handle, const char *package, size_t len) +{ + const uint32_t CharsPerLine = 29u; + St7735Context *lcd = (St7735Context *)handle; + + const uint32_t TextAreaBgColor = BGRColorGrey; + const uint32_t TextAreaFgColor = BGRColorBlack; + // Clean the botton of the display. + lcd_fill_rect( + lcd, 0, 50, lcd->parent.width, lcd->parent.height - 50, TextAreaBgColor); + + // Break the result message into several lines if it is too long to fit on + // one line. + char line_content[CharsPerLine + 1]; + const char *cursor = package; + size_t line_length = 0u; + size_t line_num = 0u; + while (len-- != 0) + { + if (isprint((int)(*cursor)) == 0) + { + line_content[line_length++] = '%'; + } + else + { + line_content[line_length++] = *cursor; + } + cursor++; + if (line_length == CharsPerLine) + { + line_content[line_length] = '\0'; + lcd_draw_str(lcd, + 1, + 55 + 10 * line_num, + LcdFontM5x7_16pt, + line_content, + TextAreaBgColor, + TextAreaFgColor); + line_length = 0; + line_num++; + } + } + // Write the final line containing the remainder of the message. + if (line_length) + { + line_content[line_length] = '\0'; + lcd_draw_str(lcd, + 1, + 55 + 10 * line_num, + LcdFontM5x7_16pt, + line_content, + TextAreaBgColor, + TextAreaFgColor); + } +} + +int main() +{ + // Initialise UART drivers for debug logging + uart0 = UART_FROM_BASE_ADDR(UART0_BASE); + uart_init(uart0); + pwm_t lcd_bl = PWM_FROM_ADDR_AND_INDEX(PWM_BASE, PWM_LCD); + + write_to_uart("\n\nInitialized UART driver\n"); + + // Initialise the timer + timer_init(); + timer_enable(SYSCLK_FREQ / 1000); + + // Initialise LCD display driver + LCD_Interface lcdInterface; + spi_t lcdSpi; + St7735Context lcd; + spi_init(&lcdSpi, LCD_SPI, LcdSpiSpeedHz); + lcd_init(&lcdSpi, lcd_bl, &lcd, &lcdInterface); + lcd_clean(&lcd, BACKGROUND_COLOR); + + size_t req_len = 8; + while (true) + { + // We allocate a big chunck of memory to temporary store a json file + // with sensitive information. Then we free the buffer without cleaning + // the content. + const size_t DbSize = 128; + char *ptr = (char *)malloc(DbSize); + read_file("clients.db", ptr, DbSize); + free(ptr); + + initial_lcd_write(&lcd); + + // Wait for the request. + get_request_length(&lcd, &req_len); + + const char *result = + run_query("SELECT name FROM animal WHERE can_fly=yes LIMIT 1"); + + // We send back the response to the request without checking that the + // requested length exeeds the needed size, which can leek information. + heartbleed(&lcd, result, req_len); + + free((void *)result); + } + + return 0; +} diff --git a/examples/heartbleed/legacy/heartbleed.h b/examples/heartbleed/legacy/heartbleed.h new file mode 100644 index 0000000..63cea63 --- /dev/null +++ b/examples/heartbleed/legacy/heartbleed.h @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef _HEARTBLEED_H_ +#define _HEARTBLEED_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "lcd.h" + +// Define our own GPIO_IN_DBNC as the version from `sonata-system` uses void +// pointer arithmetic, which clang-tidy forbids. +#define GPIO_IN_DBNC_AM GPIO_FROM_BASE_ADDR((GPIO_BASE + GPIO_IN_DBNC_REG)) + +#define CPU_TIMER_HZ (40 * 1000 * 1000) // 40 MHz + +static uart_t uart0; + +/** + * A function that writes a string to the UART console, wrapping a call to + * `vsnprintf` such that (at least) unsigned integer formatting specifier + * arguments can be provided alongside a format string. + */ +void write_to_uart(const char *format, ...) +{ + // Disable interrupts whilst outputting on UART to prevent the output for + // RX IRQ from happening simultaneously + arch_local_irq_disable(); + char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, 1024, format, args); + va_end(args); + putstr(buffer); + arch_local_irq_enable(); +} + +#define LOG_PREFIX "Heartbleed" +#define DEBUG_LOG(...) \ + if (DEBUG_DEMO) \ + { \ + write_to_uart((LOG_PREFIX)); \ + write_to_uart(__VA_ARGS__); \ + } + +#define rdcycle64 get_mcycle + +/** + * @brief Read the current GPIO joystick state. + * + * @returns The current joystick state as a byte, where each of the 5 least + * significant bits corresponds to a given joystick input. + */ +uint8_t read_joystick() +{ + return ((uint8_t)(read_gpio(GPIO_IN_DBNC_AM) >> 8u) & 0x1f); +} + +// Possible GPIO inputs for the joystick, and which GPIO bit they correspond to +enum JoystickDir +{ + JoystickLeft = 1 << 0, + JoystickUp = 1 << 1, + JoystickPressed = 1 << 2, + JoystickDown = 1 << 3, + JoystickRight = 1 << 4, +}; + +/** + * Checks whether a given joystick input is in a certain direction or not + * (where direction can also be "Pressed", i.e. the joystick is pressed down). + * + * `joystick` is the state of the joystick packed in the bits of a byte. + * `direction` is the specific direction bit to test for. + */ +bool joystick_in_direction(uint8_t joystick, enum JoystickDir direction) +{ + return (joystick & ((uint8_t)direction)) > 0; +}; + +bool joystick_is_pressed(uint8_t joystick) +{ + return (joystick & JoystickPressed) > 0; +} + +#endif //_HEARTBLEED_H_ diff --git a/examples/heartbleed/legacy/lcd.c b/examples/heartbleed/legacy/lcd.c new file mode 100644 index 0000000..9844e58 --- /dev/null +++ b/examples/heartbleed/legacy/lcd.c @@ -0,0 +1,143 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "lcd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Define our own GPIO_OUT as the version from `sonata-system` uses void +// pointer arithmetic, which clang-tidy forbids. +#define GPIO_OUT_LCD GPIO_FROM_BASE_ADDR((GPIO_BASE + GPIO_OUT_REG)) + +static void timer_delay(uint32_t ms) +{ + uint32_t timeout = get_elapsed_time() + ms; + while (get_elapsed_time() < timeout) + { + __asm__ volatile("wfi"); + } +} + +static uint32_t spi_write(void *handle, uint8_t *data, size_t len) +{ + spi_tx((spi_t *)handle, data, len); + spi_wait_idle((spi_t *)handle); + return len; +} + +static uint32_t gpio_write(void *handle, bool cs, bool dc) +{ + spi_set_cs((spi_t *)handle, LcdDcPin, dc); + spi_set_cs((spi_t *)handle, LcdCsPin, cs); + return 0; +} + +int lcd_init(spi_t *spi, + pwm_t backlight, + St7735Context *lcd, + LCD_Interface *interface) +{ + // Set the initial state of the LCD control pins + spi_set_cs(spi, LcdDcPin, 0x00); + spi_set_cs(spi, LcdCsPin, 0x00); + + // Reset the LCD + set_output_bit(GPIO_OUT_LCD, LcdRstPin, 0x0); + timer_delay(150); + set_output_bit(GPIO_OUT_LCD, LcdRstPin, 0x1); + + // Init LCD Driver, and set the SPI driver + interface->handle = spi; // SPI handle. + interface->spi_write = spi_write; // SPI write callback. + interface->gpio_write = gpio_write; // GPIO write callback. + interface->timer_delay = timer_delay; // Timer delay callback. + lcd_st7735_init(lcd, interface); + + // Set the LCD orientation + lcd_st7735_set_orientation(lcd, LCD_Rotate180); + + // Setup text font bitmaps to be used and the colors. + lcd_st7735_set_font(lcd, &m3x6_16ptFont); + lcd_st7735_set_font_colors(lcd, BGRColorWhite, BGRColorBlack); + + // Clean display with a white rectangle. + lcd_st7735_clean(lcd); + set_pwm(backlight, 1, 255); + + return 0; +} + +void lcd_draw_str(St7735Context *lcd, + uint32_t x, + uint32_t y, + LcdFont font, + const char *format, + uint32_t backgroundColour, + uint32_t textColour, + ...) +{ + // Format the provided string + char buffer[1024]; + va_list args; + va_start(args, textColour); + vsnprintf(buffer, 1024, format, args); + va_end(args); + + Font stringFont; + switch (font) + { + case LcdFontLucidaConsole_10pt: + stringFont = lucidaConsole_10ptFont; + break; + case LcdFontLucidaConsole_12pt: + stringFont = lucidaConsole_12ptFont; + break; + default: + stringFont = m3x6_16ptFont; + } + lcd_st7735_set_font(lcd, &stringFont); + lcd_st7735_set_font_colors(lcd, backgroundColour, textColour); + lcd_st7735_puts(lcd, (LCD_Point){x, y}, buffer); +} + +void lcd_clean(St7735Context *lcd, uint32_t color) +{ + size_t w, h; + lcd_st7735_get_resolution(lcd, &h, &w); + LCD_rectangle rect = {(LCD_Point){0, 0}, w, h}; + lcd_st7735_fill_rectangle(lcd, rect, color); +} + +void lcd_fill_rect(St7735Context *lcd, + uint32_t x, + uint32_t y, + uint32_t w, + uint32_t h, + uint32_t color) +{ + LCD_rectangle rect = {(LCD_Point){x, y}, w, h}; + lcd_st7735_fill_rectangle(lcd, rect, color); +} + +void lcd_draw_img(St7735Context *lcd, + uint32_t x, + uint32_t y, + uint32_t w, + uint32_t h, + const uint8_t *data) +{ + LCD_rectangle rect = {(LCD_Point){x, y}, w, h}; + lcd_st7735_draw_rgb565(lcd, rect, data); +} diff --git a/examples/heartbleed/legacy/lcd.h b/examples/heartbleed/legacy/lcd.h new file mode 100644 index 0000000..ece6fc8 --- /dev/null +++ b/examples/heartbleed/legacy/lcd.h @@ -0,0 +1,133 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef _HEARTBLEED_LCD_H_ +#define _HEARTBLEED_LCD_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + // Constants. + enum + { + // Pin out mapping. + LcdCsPin = 0, + LcdDcPin, + LcdRstPin, + LcdMosiPin, + LcdSclkPin, + // Spi clock rate. + LcdSpiSpeedHz = 5 * 100 * 1000, + }; + + enum + { + BGRColorBlack = 0x000000, + BGRColorWhite = 0xFFFFFF, + BGRColorBlue = 0xFF0000, + BGRColorGreen = 0x00FF00, + BGRColorRed = 0x0000FF, + }; + + // Fonts available for LCD rendering + typedef enum LcdFont + { + LcdFontM3x6_16pt, // NOLINT + LcdFontLucidaConsole_10pt, // NOLINT + LcdFontLucidaConsole_12pt, // NOLINT + } LcdFont; + + /** + * @brief Initialise the LCD, starting and clearing the screen. + * + * @param spi contains an initialized SPI handle to use to write to the LCD. + * @param backlight contains an initialized pwm handle to use to control the + * LCD backlight. + * @param lcd is an outparam to store the initialized LCD handle. + * @param interface is an outparam storing the initialized LCD interface. + */ + int lcd_init(spi_t *spi, + pwm_t backlight, + St7735Context *lcd, + LCD_Interface *interface); + + /** + * @brief Formats and draws a string to the LCD display based upon the + * provided formatting and display information. The string can use + * formatting specifiers as defined by `vsnprintf(3)`. + * + * @param lcd a handle to the LCD to draw to. + * @param x is the X-coordinate on the LCD of the top left of the string. + * @param y is the Y-coordinate on the LCD of the top left of the string. + * @param font is the font to render the string with. + * @param format is the formatting string to write. + * @param backgroundColour is the colour to use for the string's background. + * @param textColour is the colour to render the text in. + * The remaining variable arguments are unsigned integers used to fill in + * the formatiting specifiers in the format string. + */ + void lcd_draw_str(St7735Context *lcd, + uint32_t x, + uint32_t y, + LcdFont font, + const char *format, + uint32_t backgroundColour, + uint32_t textColour, + ...); + + /** + * @brief Clean the LCD display with a given colour. + * + * @param lcd a handle to the LCD to draw to + * @param color is the 32-bit colour to display. + */ + void lcd_clean(St7735Context *lcd, uint32_t color); + + /** + * @brief Draws a rectangle of a given colour on the LCD. + * + * @param lcd a handle to the LCD to draw to. + * @param x is the X-position of the top-left corner of the rectangle. + * @param y is the Y-position of the top-left corner of the rectangle. + * @param w is the width of the rectangle. + * @param h is the height of the rectangle. + * @param color is the 32-bit colour to fill the rectangle with. + */ + void lcd_fill_rect(St7735Context *lcd, + uint32_t x, + uint32_t y, + uint32_t w, + uint32_t h, + uint32_t color); + /** + * @brief Draw an image to the LCD. + * + * @param lcd a handle to the LCD to draw to. + * @param x is the X-position of the top-left corner of the image on the + * LCD. + * @param y is the Y-position of the top-left corner of the image on the + * LCD. + * @param w is the width of the image. + * @param h is the height of the image. + * @param data is the byte array containing the image data, of size at least + * of `w` * `h`, where each value is a RGB565 colour value. + */ + void lcd_draw_img(St7735Context *lcd, + uint32_t x, + uint32_t y, + uint32_t w, + uint32_t h, + const uint8_t *data); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // _HEARTBLEED_LCD_H_ diff --git a/examples/heartbleed/legacy/xmake.lua b/examples/heartbleed/legacy/xmake.lua new file mode 100644 index 0000000..c3eb22e --- /dev/null +++ b/examples/heartbleed/legacy/xmake.lua @@ -0,0 +1,102 @@ +-- Copyright lowRISC Contributors. +-- SPDX-License-Identifier: Apache-2.0 + +set_project("Sonata Heartbleed Demo Legacy Version") + +-- The directory containing this script +local scriptdir = os.scriptdir() +-- The root of the `sonata-software` repository +local rootdir = path.join(scriptdir, '../../..') +-- The directory containing the legacy baremetal build files (link script, runtime, drivers) +local legacy_dir = path.join(rootdir, "third_party/sonata-system/sw/legacy") + +set_defaultarchs("cheriot") +set_defaultplat("cheriot") +includes("../../../common.lua") + +-- Setup the GCC toolchain for building non-CHERI baremetal programs +toolchain("legacy-gcc") + set_kind("standalone") + set_toolset("cc", "riscv32-unknown-elf-gcc") + set_toolset("cxx", "riscv32-unknown-elf-g++") + set_toolset("objcopy", "riscv32-unknown-elf-objcopy") + set_toolset("ld", "riscv32-unknown-elf-gcc") + set_toolset("as", "riscv32-unknown-elf-gcc") + set_toolset("ar", "riscv32-unknown-elf-ar") + + -- Set up flags needed in the toolchain + on_load(function (toolchain) + local linkscript = path.join(legacy_dir, "link.ld") + -- Flags used for C/C++, assembly and the linker + local default_flags = { + "-march=rv32imc", + "-mabi=ilp32", + "-mcmodel=medany", + "-Wall", + "-fvisibility=hidden" + } + -- C/C++ flags + toolchain:add("cxflags", default_flags, {force = true}) + toolchain:add("cflags", default_flags) + -- Assembly flags + toolchain:add("asflags", default_flags) + -- Linker flags + toolchain:add("ldflags", default_flags) + toolchain:add("ldflags", "-ffreestanding -g -nostartfiles -T " .. linkscript) + end) +toolchain_end() + +-- Helper function to define legacy firmware (running without CHERI or CHERIoT-RTOS, baremetal) +-- Useful for comparing between CHERI and non-CHERI for e.g. demos/exercises +function legacy_firmware(name) + + -- Create the firmware target; remains open on return, so the caller can + -- add more rules to it as they like. + target(name) + set_kind("binary") + set_arch("rv32imc") + set_plat("ilp32") + set_languages("gnu99", "gnuxx11") + -- Add the basic C runtime to the baremetal firmware + add_files( + path.join(legacy_dir, "common/crt0.S"), + path.join(legacy_dir, "common/gpio.c"), + path.join(legacy_dir, "common/i2c.c"), + path.join(legacy_dir, "common/pwm.c"), + path.join(legacy_dir, "common/rv_plic.c"), + path.join(legacy_dir, "common/sonata_system.c"), + path.join(legacy_dir, "common/spi.c"), + path.join(legacy_dir, "common/timer.c"), + path.join(legacy_dir, "common/uart.c") + ) + after_link(convert_to_vmem) +end +set_toolchains("legacy-gcc") + +-- Legacy-specific LCD library for using the display drivers +target("lcd_st7735_lib_am") + set_kind("static") + local lcd_dir = path.join(rootdir, "third_party/display_drivers") + add_files( + path.join(lcd_dir, "core/lcd_base.c"), + path.join(lcd_dir, "core/lucida_console_10pt.c"), + path.join(lcd_dir, "core/lucida_console_12pt.c"), + path.join(lcd_dir, "core/m3x6_16pt.c"), + path.join(lcd_dir, "st7735/lcd_st7735.c") + ) + +-- Heartbleed demo: Sending Firmware (non-CHERIoT version) +legacy_firmware("heartbleed_demo_legacy") + add_deps("lcd_st7735_lib_am") + add_includedirs( + "../../../third_party/display_drivers/core/", + "../../../third_party/display_drivers/st7735/", + "../../../third_party/sonata-system/sw/legacy/common/", + "../../../libraries/" + ) + add_ldflags("-specs=nosys.specs", {force = true}) + add_files( + "../../../third_party/sonata-system/vendor/cheriot_debug_module/tb/prog/syscalls.c", + "../../../libraries/m5x7_16pt.c" + ) + add_files("heartbleed.c", "../common.c", "lcd.c") diff --git a/examples/heartbleed/xmake.lua b/examples/heartbleed/xmake.lua new file mode 100644 index 0000000..817460c --- /dev/null +++ b/examples/heartbleed/xmake.lua @@ -0,0 +1,4 @@ +-- Copyright lowRISC Contributors. +-- SPDX-License-Identifier: Apache-2.0 + +includes("cheriot") diff --git a/examples/xmake.lua b/examples/xmake.lua index 557aed2..192cfd2 100644 --- a/examples/xmake.lua +++ b/examples/xmake.lua @@ -13,7 +13,7 @@ includes("../common.lua") option("board") set_default("sonata-1.1") -includes("all", "snake", "automotive") +includes("all", "snake", "automotive", "heartbleed") -- A simple demo using only devices on the Sonata board firmware("sonata_simple_demo") diff --git a/libraries/m5x7_16pt.c b/libraries/m5x7_16pt.c new file mode 100644 index 0000000..ffb6cbb --- /dev/null +++ b/libraries/m5x7_16pt.c @@ -0,0 +1,1164 @@ +// Copyright lowRISC Contributors. +// SPDX-License-Identifier: Apache-2.0 +// +// This is a rasterized version of the m5x7 font. +// The m5x7 font is created by Daniel Linssen and is free to use with +// attribution. The original TTF font can be found at +// https://managore.itch.io/m5x7 + +#include +// Character bitmaps for m5x7_16pt +const unsigned char m5x7_16ptBitmaps[] = { + // @32 ' ' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @33 '!' (2 pixels wide) + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x00, // + 0x01, // # + 0x00, // + 0x00, // + + // @34 '"' (4 pixels wide) + 0x05, // # # + 0x05, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @35 '#' (6 pixels wide) + 0x00, // + 0x0a, // # # + 0x1f, // ##### + 0x0a, // # # + 0x0a, // # # + 0x1f, // ##### + 0x0a, // # # + 0x00, // + 0x00, // + + // @36 '$' (6 pixels wide) + 0x04, // # + 0x1e, // #### + 0x05, // # # + 0x0e, // ### + 0x14, // # # + 0x0f, // #### + 0x04, // # + 0x00, // + 0x00, // + + // @37 '%' (6 pixels wide) + 0x13, // ## # + 0x13, // ## # + 0x08, // # + 0x04, // # + 0x02, // # + 0x19, // # ## + 0x19, // # ## + 0x00, // + 0x00, // + + // @38 '&' (7 pixels wide) + 0x04, // # + 0x0a, // # # + 0x0a, // # # + 0x06, // ## + 0x11, // # # + 0x11, // # # + 0x2e, // ### # + 0x00, // + 0x00, // + + // @39 ''' (2 pixels wide) + 0x01, // # + 0x01, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @40 '(' (3 pixels wide) + 0x02, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x02, // # + 0x00, // + + // @41 ')' (3 pixels wide) + 0x01, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x01, // # + 0x00, // + + // @42 '*' (4 pixels wide) + 0x05, // # # + 0x02, // # + 0x05, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @43 '+' (6 pixels wide) + 0x00, // + 0x04, // # + 0x04, // # + 0x1f, // ##### + 0x04, // # + 0x04, // # + 0x00, // + 0x00, // + 0x00, // + + // @44 ',' (3 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x02, // # + 0x01, // # + 0x00, // + + // @45 '-' (6 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x1f, // ##### + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @46 '.' (2 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x01, // # + 0x00, // + 0x00, // + + // @47 '/' (4 pixels wide) + 0x04, // # + 0x04, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x01, // # + 0x01, // # + 0x00, // + 0x00, // + + // @48 '0' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @49 '1' (6 pixels wide) + 0x04, // # + 0x06, // ## + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x1f, // ##### + 0x00, // + 0x00, // + + // @50 '2' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x10, // # + 0x08, // # + 0x04, // # + 0x02, // # + 0x1f, // ##### + 0x00, // + 0x00, // + + // @51 '3' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x10, // # + 0x0c, // ## + 0x10, // # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @52 '4' (6 pixels wide) + 0x08, // # + 0x0c, // ## + 0x0a, // # # + 0x09, // # # + 0x1f, // ##### + 0x08, // # + 0x08, // # + 0x00, // + 0x00, // + + // @53 '5' (6 pixels wide) + 0x1f, // ##### + 0x01, // # + 0x0f, // #### + 0x10, // # + 0x10, // # + 0x10, // # + 0x0f, // #### + 0x00, // + 0x00, // + + // @54 '6' (6 pixels wide) + 0x0e, // ### + 0x01, // # + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @55 '7' (6 pixels wide) + 0x1f, // ##### + 0x10, // # + 0x10, // # + 0x08, // # + 0x08, // # + 0x04, // # + 0x04, // # + 0x00, // + 0x00, // + + // @56 '8' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @57 '9' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x1e, // #### + 0x10, // # + 0x0e, // ### + 0x00, // + 0x00, // + + // @58 ':' (2 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x01, // # + 0x00, // + 0x00, // + 0x01, // # + 0x00, // + 0x00, // + + // @59 ';' (3 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x02, // # + 0x00, // + 0x00, // + 0x02, // # + 0x01, // # + 0x00, // + + // @60 '<' (4 pixels wide) + 0x00, // + 0x04, // # + 0x02, // # + 0x01, // # + 0x02, // # + 0x04, // # + 0x00, // + 0x00, // + 0x00, // + + // @61 '=' (5 pixels wide) + 0x00, // + 0x00, // + 0x0f, // #### + 0x00, // + 0x0f, // #### + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @62 '>' (4 pixels wide) + 0x00, // + 0x01, // # + 0x02, // # + 0x04, // # + 0x02, // # + 0x01, // # + 0x00, // + 0x00, // + 0x00, // + + // @63 '?' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x10, // # + 0x08, // # + 0x04, // # + 0x00, // + 0x04, // # + 0x00, // + 0x00, // + + // @64 '@' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x1d, // # ### + 0x15, // # # # + 0x1d, // # ### + 0x01, // # + 0x1e, // #### + 0x00, // + 0x00, // + + // @65 'A' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x1f, // ##### + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @66 'B' (6 pixels wide) + 0x0f, // #### + 0x11, // # # + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0f, // #### + 0x00, // + 0x00, // + + // @67 'C' (6 pixels wide) + 0x1c, // ### + 0x02, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x1e, // #### + 0x00, // + 0x00, // + + // @68 'D' (6 pixels wide) + 0x07, // ### + 0x09, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0f, // #### + 0x00, // + 0x00, // + + // @69 'E' (6 pixels wide) + 0x1e, // #### + 0x01, // # + 0x0f, // #### + 0x01, // # + 0x01, // # + 0x01, // # + 0x1e, // #### + 0x00, // + 0x00, // + + // @70 'F' (6 pixels wide) + 0x1e, // #### + 0x01, // # + 0x0f, // #### + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x00, // + 0x00, // + + // @71 'G' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x01, // # + 0x1d, // # ### + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @72 'H' (6 pixels wide) + 0x11, // # # + 0x11, // # # + 0x1f, // ##### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @73 'I' (6 pixels wide) + 0x1f, // ##### + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x1f, // ##### + 0x00, // + 0x00, // + + // @74 'J' (6 pixels wide) + 0x1f, // ##### + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x08, // # + 0x07, // ### + 0x00, // + 0x00, // + + // @75 'K' (6 pixels wide) + 0x11, // # # + 0x11, // # # + 0x09, // # # + 0x07, // ### + 0x09, // # # + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @76 'L' (6 pixels wide) + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x1e, // #### + 0x00, // + 0x00, // + + // @77 'M' (8 pixels wide) + 0x36, // ## ## + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x41, // # # + 0x00, // + 0x00, // + + // @78 'N' (6 pixels wide) + 0x07, // ### + 0x09, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @79 'O' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @80 'P' (6 pixels wide) + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0f, // #### + 0x01, // # + 0x01, // # + 0x00, // + 0x00, // + + // @81 'Q' (6 pixels wide) + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x15, // # # # + 0x09, // # # + 0x16, // ## # + 0x00, // + 0x00, // + + // @82 'R' (6 pixels wide) + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @83 'S' (6 pixels wide) + 0x1e, // #### + 0x01, // # + 0x01, // # + 0x0e, // ### + 0x10, // # + 0x10, // # + 0x0f, // #### + 0x00, // + 0x00, // + + // @84 'T' (6 pixels wide) + 0x1f, // ##### + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x00, // + 0x00, // + + // @85 'U' (6 pixels wide) + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @86 'V' (6 pixels wide) + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0a, // # # + 0x04, // # + 0x00, // + 0x00, // + + // @87 'W' (8 pixels wide) + 0x41, // # # + 0x41, // # # + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x36, // ## ## + 0x00, // + 0x00, // + + // @88 'X' (6 pixels wide) + 0x11, // # # + 0x11, // # # + 0x0a, // # # + 0x04, // # + 0x0a, // # # + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @89 'Y' (6 pixels wide) + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0a, // # # + 0x04, // # + 0x04, // # + 0x04, // # + 0x00, // + 0x00, // + + // @90 'Z' (6 pixels wide) + 0x1f, // ##### + 0x10, // # + 0x08, // # + 0x04, // # + 0x02, // # + 0x01, // # + 0x1f, // ##### + 0x00, // + 0x00, // + + // @91 '[' (3 pixels wide) + 0x03, // ## + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x03, // ## + 0x00, // + + // @92 '\' (4 pixels wide) + 0x01, // # + 0x01, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x04, // # + 0x04, // # + 0x00, // + 0x00, // + + // @93 ']' (3 pixels wide) + 0x03, // ## + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x03, // ## + 0x00, // + + // @94 '^' (6 pixels wide) + 0x04, // # + 0x0a, // # # + 0x11, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @95 '_' (6 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x1f, // ##### + 0x00, // + 0x00, // + + // @96 '`' (3 pixels wide) + 0x01, // # + 0x02, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @97 'a' (6 pixels wide) + 0x00, // + 0x00, // + 0x0e, // ### + 0x10, // # + 0x1e, // #### + 0x11, // # # + 0x1e, // #### + 0x00, // + 0x00, // + + // @98 'b' (6 pixels wide) + 0x01, // # + 0x01, // # + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0f, // #### + 0x00, // + 0x00, // + + // @99 'c' (5 pixels wide) + 0x00, // + 0x00, // + 0x0e, // ### + 0x01, // # + 0x01, // # + 0x01, // # + 0x0e, // ### + 0x00, // + 0x00, // + + // @100 'd' (6 pixels wide) + 0x10, // # + 0x10, // # + 0x1e, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x1e, // #### + 0x00, // + 0x00, // + + // @101 'e' (6 pixels wide) + 0x00, // + 0x00, // + 0x0e, // ### + 0x11, // # # + 0x1f, // ##### + 0x01, // # + 0x0e, // ### + 0x00, // + 0x00, // + + // @102 'f' (5 pixels wide) + 0x0c, // ## + 0x02, // # + 0x0f, // #### + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x00, // + 0x00, // + + // @103 'g' (6 pixels wide) + 0x00, // + 0x00, // + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x1e, // #### + 0x10, // # + 0x0e, // ### + + // @104 'h' (6 pixels wide) + 0x01, // # + 0x01, // # + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @105 'i' (3 pixels wide) + 0x02, // # + 0x00, // + 0x03, // ## + 0x02, // # + 0x02, // # + 0x02, // # + 0x02, // # + 0x00, // + 0x00, // + + // @106 'j' (4 pixels wide) + 0x04, // # + 0x00, // + 0x06, // ## + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x04, // # + 0x03, // ## + + // @107 'k' (5 pixels wide) + 0x01, // # + 0x01, // # + 0x09, // # # + 0x05, // # # + 0x03, // ## + 0x05, // # # + 0x09, // # # + 0x00, // + 0x00, // + + // @108 'l' (3 pixels wide) + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x02, // # + 0x00, // + 0x00, // + + // @109 'm' (8 pixels wide) + 0x00, // + 0x00, // + 0x3d, // # #### + 0x4b, // ## # # + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x00, // + 0x00, // + + // @110 'n' (6 pixels wide) + 0x00, // + 0x00, // + 0x0d, // # ## + 0x13, // ## # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @111 'o' (6 pixels wide) + 0x00, // + 0x00, // + 0x0e, // ### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0e, // ### + 0x00, // + 0x00, // + + // @112 'p' (6 pixels wide) + 0x00, // + 0x00, // + 0x0f, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0f, // #### + 0x01, // # + 0x01, // # + + // @113 'q' (6 pixels wide) + 0x00, // + 0x00, // + 0x1e, // #### + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x1e, // #### + 0x10, // # + 0x10, // # + + // @114 'r' (5 pixels wide) + 0x00, // + 0x00, // + 0x0d, // # ## + 0x03, // ## + 0x01, // # + 0x01, // # + 0x01, // # + 0x00, // + 0x00, // + + // @115 's' (6 pixels wide) + 0x00, // + 0x00, // + 0x1e, // #### + 0x01, // # + 0x0e, // ### + 0x10, // # + 0x0f, // #### + 0x00, // + 0x00, // + + // @116 't' (5 pixels wide) + 0x02, // # + 0x02, // # + 0x0f, // #### + 0x02, // # + 0x02, // # + 0x02, // # + 0x0c, // ## + 0x00, // + 0x00, // + + // @117 'u' (6 pixels wide) + 0x00, // + 0x00, // + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x19, // # ## + 0x16, // ## # + 0x00, // + 0x00, // + + // @118 'v' (6 pixels wide) + 0x00, // + 0x00, // + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x0a, // # # + 0x04, // # + 0x00, // + 0x00, // + + // @119 'w' (8 pixels wide) + 0x00, // + 0x00, // + 0x41, // # # + 0x49, // # # # + 0x49, // # # # + 0x49, // # # # + 0x36, // ## ## + 0x00, // + 0x00, // + + // @120 'x' (6 pixels wide) + 0x00, // + 0x00, // + 0x11, // # # + 0x0a, // # # + 0x04, // # + 0x0a, // # # + 0x11, // # # + 0x00, // + 0x00, // + + // @121 'y' (6 pixels wide) + 0x00, // + 0x00, // + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x11, // # # + 0x1e, // #### + 0x10, // # + 0x0e, // ### + + // @122 'z' (6 pixels wide) + 0x00, // + 0x00, // + 0x1f, // ##### + 0x08, // # + 0x04, // # + 0x02, // # + 0x1f, // ##### + 0x00, // + 0x00, // + + // @123 '{' (4 pixels wide) + 0x06, // ## + 0x02, // # + 0x02, // # + 0x03, // ## + 0x02, // # + 0x02, // # + 0x02, // # + 0x06, // ## + 0x00, // + + // @124 '|' (2 pixels wide) + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x01, // # + 0x00, // + 0x00, // + + // @125 '}' (4 pixels wide) + 0x03, // ## + 0x02, // # + 0x02, // # + 0x06, // ## + 0x02, // # + 0x02, // # + 0x02, // # + 0x03, // ## + 0x00, // + + // @126 '~' (6 pixels wide) + 0x00, // + 0x00, // + 0x02, // # + 0x15, // # # # + 0x08, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // +}; + +// Character descriptors for 16pt +const FontCharInfo m5x7_16ptDescriptors[] = { + {5, 0}, // ' ' + {2, 9}, // '!' + {4, 18}, // '"' + {6, 27}, // '#' + {6, 36}, // '$' + {6, 45}, // '%' + {7, 54}, // '&' + {2, 63}, // ''' + {3, 72}, // '(' + {3, 81}, // ')' + {4, 90}, // '*' + {6, 99}, // '+' + {3, 108}, // ',' + {6, 117}, // '-' + {2, 126}, // '.' + {4, 135}, // '/' + {6, 144}, // '0' + {6, 153}, // '1' + {6, 162}, // '2' + {6, 171}, // '3' + {6, 180}, // '4' + {6, 189}, // '5' + {6, 198}, // '6' + {6, 207}, // '7' + {6, 216}, // '8' + {6, 225}, // '9' + {2, 234}, // ':' + {3, 243}, // ';' + {4, 252}, // '<' + {5, 261}, // '=' + {4, 270}, // '>' + {6, 279}, // '?' + {6, 288}, // '@' + {6, 297}, // 'A' + {6, 306}, // 'B' + {6, 315}, // 'C' + {6, 324}, // 'D' + {6, 333}, // 'E' + {6, 342}, // 'F' + {6, 351}, // 'G' + {6, 360}, // 'H' + {6, 369}, // 'I' + {6, 378}, // 'J' + {6, 387}, // 'K' + {6, 396}, // 'L' + {8, 405}, // 'M' + {6, 414}, // 'N' + {6, 423}, // 'O' + {6, 432}, // 'P' + {6, 441}, // 'Q' + {6, 450}, // 'R' + {6, 459}, // 'S' + {6, 468}, // 'T' + {6, 477}, // 'U' + {6, 486}, // 'V' + {8, 495}, // 'W' + {6, 504}, // 'X' + {6, 513}, // 'Y' + {6, 522}, // 'Z' + {3, 531}, // '[' + {4, 540}, // '\' + {3, 549}, // ']' + {6, 558}, // '^' + {6, 567}, // '_' + {3, 576}, // '`' + {6, 585}, // 'a' + {6, 594}, // 'b' + {5, 603}, // 'c' + {6, 612}, // 'd' + {6, 621}, // 'e' + {5, 630}, // 'f' + {6, 639}, // 'g' + {6, 648}, // 'h' + {3, 657}, // 'i' + {4, 666}, // 'j' + {5, 675}, // 'k' + {3, 684}, // 'l' + {8, 693}, // 'm' + {6, 702}, // 'n' + {6, 711}, // 'o' + {6, 720}, // 'p' + {6, 729}, // 'q' + {5, 738}, // 'r' + {6, 747}, // 's' + {5, 756}, // 't' + {6, 765}, // 'u' + {6, 774}, // 'v' + {8, 783}, // 'w' + {6, 792}, // 'x' + {6, 801}, // 'y' + {6, 810}, // 'z' + {4, 819}, // '{' + {2, 828}, // '|' + {4, 837}, // '}' + {6, 846}, // '~' +}; + +// Font information for 16pt +const Font m5x7_16ptFont = { + 9, // Character height + ' ', // Start character + '~', // End character + m5x7_16ptDescriptors, // Character descriptor array + m5x7_16ptBitmaps, // Character bitmap array +}; diff --git a/libraries/m5x7_16pt.h b/libraries/m5x7_16pt.h new file mode 100644 index 0000000..dcd7c33 --- /dev/null +++ b/libraries/m5x7_16pt.h @@ -0,0 +1,20 @@ +// Copyright lowRISC Contributors. +// SPDX-License-Identifier: Apache-2.0 +// +// This is a rasterized version of the m5x7 font. +// The m5x7 font is created by Daniel Linssen and is free to use with +// attribution. The original TTF font can be found at +// https://managore.itch.io/m5x7 + +#ifndef M5X7_16PT_H_ +#define M5X7_16PT_H_ + +#include + +#include "font.h" + +extern const unsigned char m5x7_16ptBitmaps[]; +extern const Font m5x7_16ptFont; +extern const FontCharInfo m5x7_16ptDescriptors[]; + +#endif /* M5X7_16PT_H_ */ diff --git a/libraries/xmake.lua b/libraries/xmake.lua index 840e63e..b039b8e 100644 --- a/libraries/xmake.lua +++ b/libraries/xmake.lua @@ -10,6 +10,9 @@ library("lcd") add_files("../third_party/display_drivers/src/core/lucida_console_12pt.c") add_files("../third_party/display_drivers/src/st7735/lcd_st7735.c") add_files("lcd.cc") + add_includedirs( + "../third_party/display_drivers/core/src/", {public = true} + ) library("sense_hat") set_default(false) diff --git a/third_party/sonata-system b/third_party/sonata-system index 275eaab..06b1a5d 160000 --- a/third_party/sonata-system +++ b/third_party/sonata-system @@ -1 +1 @@ -Subproject commit 275eaab74ebec4fcf045b071eb834e586d1d28f5 +Subproject commit 06b1a5dca1581764cd36403cefc34e72acc6aa2e