Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
Yann MAGNIN | 49508b417c | |
Yann MAGNIN | b316b98004 | |
Yann MAGNIN | 4ea5758074 | |
Yann MAGNIN | d596c74634 | |
Yann MAGNIN | cefccb8bb1 | |
Yann MAGNIN | 584e95e11b | |
Yann MAGNIN | 18c917f257 | |
Yann MAGNIN | f75210e0fb | |
Yann MAGNIN | 8acbd8bceb | |
Yann MAGNIN | 73711a76b2 | |
Yann MAGNIN | b3e1a6ed6c | |
Yann MAGNIN | ac38d420f7 | |
Yann MAGNIN | 74571c7882 | |
Yann MAGNIN | 597b25682a | |
Yann MAGNIN | 18e895dfb7 | |
Yann MAGNIN | c6aa4b5ac4 | |
Yann MAGNIN | 42c3fabb34 | |
Yann MAGNIN | 3a42d0e055 | |
Yann MAGNIN | 5ea6e33f46 |
47
.nvimrc
47
.nvimrc
|
@ -1,12 +1,51 @@
|
|||
" indicate the compiler to use
|
||||
let g:ale_asm_gcc_executable = 'sh-elf-vhex-gcc'
|
||||
let g:ale_c_gcc_executable = 'sh-elf-vhex-gcc'
|
||||
let g:ale_c_cc_executable = 'sh-elf-vhex-gcc'
|
||||
let g:ale_cpp_gcc_executable = 'sh-elf-vhex-gcc'
|
||||
|
||||
" Setup GCC as linter
|
||||
"let g:ale_linters = {'c' : ['sh-elf-vhex-gcc']}
|
||||
" Find GiteaPC include directory information
|
||||
let s:_hdrflags = [
|
||||
\ '-I./vxgos/bootloader/include',
|
||||
\ '-I./vxgos/bootloader/boards/fxcg50/include/',
|
||||
\ '-I./vxgos/bootloader/boards/raspi3b/include/',
|
||||
\ ]
|
||||
let s:_cflags = [
|
||||
\ '-DFXCG50',
|
||||
\ '-std=c11',
|
||||
\ '-Wall',
|
||||
\ '-Wextra',
|
||||
\ ]
|
||||
let s:_asmflags = [
|
||||
\ '-DFXCG50',
|
||||
\ '-x assembler-with-cpp',
|
||||
\ '-Wall',
|
||||
\ '-Wextra',
|
||||
\ '-m4-nofpu',
|
||||
\ '-mb',
|
||||
\ '-Wa,--dsp',
|
||||
\ ]
|
||||
|
||||
" Patch include header
|
||||
let g:ale_c_cc_options = '-std=c11 -Wall -I./kernel/include -I./boards/*/include'
|
||||
" convert table into string
|
||||
let s:_hdrflags = join(s:_hdrflags)
|
||||
let s:_asmflags = join(s:_asmflags)
|
||||
let s:_cflags = join(s:_cflags)
|
||||
|
||||
" Patch option
|
||||
" @note
|
||||
" - we setup cpp because header file is considered ad C++ file
|
||||
" - we force sh-elf-vhex for assembly file only
|
||||
let g:ale_asm_gcc_options = s:_asmflags.' '.s:_hdrflags
|
||||
let g:ale_c_gcc_options = s:_cflags.' '.s:_hdrflags
|
||||
let g:ale_c_cc_options = s:_cflags.' '.s:_hdrflags
|
||||
let g:ale_cpp_cc_options = s:_cflags.' '.s:_hdrflags
|
||||
let g:ale_cpp_gcc_options = s:_cflags.' '.s:_hdrflags
|
||||
|
||||
" Enable completion
|
||||
let g:ale_completion_enabled = 1
|
||||
|
||||
" Enable linter when enter is pressed
|
||||
let g:ale_lint_on_enter = 1
|
||||
|
||||
" Force display non-printable chars
|
||||
set list
|
||||
|
|
109
CMakeLists.txt
109
CMakeLists.txt
|
@ -1,109 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
|
||||
#---
|
||||
# handle vxSDK-specific
|
||||
#---
|
||||
|
||||
if(
|
||||
NOT DEFINED ENV{VXSDK_PKG_NAME}
|
||||
OR NOT DEFINED ENV{VXSDK_PKG_VERSION}
|
||||
OR NOT DEFINED ENV{VXSDK_BUILD_CFLAGS}
|
||||
OR NOT DEFINED ENV{VXSDK_PREFIX_BUILD}
|
||||
OR NOT DEFINED ENV{VXSDK_PREFIX_INSTALL})
|
||||
message(FATAL_ERROR "You should use the vxSDK to build this project")
|
||||
endif()
|
||||
|
||||
set(VXSDK_PKG_NAME $ENV{VXSDK_PKG_NAME})
|
||||
set(VXSDK_PKG_VERSION $ENV{VXSDK_PKG_VERSION})
|
||||
set(VXSDK_PREFIX_BUILD $ENV{VXSDK_PREFIX_BUILD})
|
||||
set(VXSDK_PREFIX_INSTALL $ENV{VXSDK_PREFIX_INSTALL})
|
||||
set(VXSDK_BUILD_CFLAGS $ENV{VXSDK_BUILD_CFLAGS})
|
||||
|
||||
|
||||
#---
|
||||
# handle configuration-specific information
|
||||
#---
|
||||
|
||||
set(VXKERNEL_CONF ${VXSDK_PREFIX_BUILD}/vxkernel_config.cmake)
|
||||
|
||||
if(NOT EXISTS ${VXKERNEL_CONF})
|
||||
message(FATAL_ERROR "You should performs project configuration")
|
||||
endif()
|
||||
|
||||
include(${VXKERNEL_CONF})
|
||||
|
||||
if(NOT DEFINED VXKERNEL_ENABLE_MODULES)
|
||||
message(FATAL_ERROR "Missing module declaration")
|
||||
elseif(NOT DEFINED VXKERNEL_ENABLE_DRIVERS)
|
||||
message(FATAL_ERROR "Missing driver declaration")
|
||||
endif()
|
||||
|
||||
|
||||
#---
|
||||
# handle project-specific information
|
||||
#---
|
||||
|
||||
project(${VXSDK_PKG_NAME} VERSION ${VXSDK_PKG_VERSION} LANGUAGES C ASM)
|
||||
|
||||
|
||||
#---
|
||||
# Handle source listing
|
||||
#---
|
||||
|
||||
set(VXKERNEL_SRC_TARGETS "")
|
||||
set(VXKERNEL_INC_TARGETS "")
|
||||
set(VXKERNEL_DIR_TARGETS "")
|
||||
|
||||
foreach(mod ${VXKERNEL_ENABLE_MODULES})
|
||||
list(APPEND VXKERNEL_DIR_TARGETS kernel/src/modules/${mod})
|
||||
endforeach()
|
||||
|
||||
foreach(drv ${VXKERNEL_ENABLE_DRIVERS})
|
||||
list(APPEND VXKERNEL_DIR_TARGETS kernel/src/drivers/${drv})
|
||||
endforeach()
|
||||
|
||||
set(board_prefix boards/${VXKERNEL_ENABLE_BOARD})
|
||||
if(EXISTS ${board_prefix}/src)
|
||||
list(APPEND VXKERNEL_DIR_TARGETS ${board_prefix}/src)
|
||||
endif()
|
||||
|
||||
foreach(dir ${VXKERNEL_DIR_TARGETS})
|
||||
file(GLOB_RECURSE dir_src ${dir}/*.c ${dir}/*.S ${dir}/*.s)
|
||||
list(APPEND VXKERNEL_SRC_TARGETS ${dir_src})
|
||||
endforeach()
|
||||
|
||||
file(GLOB dir_src kernel/src/*.c kernel/src/*.S kernel/src/*.s)
|
||||
list(APPEND VXKERNEL_SRC_TARGETS ${dir_src})
|
||||
|
||||
list(APPEND VXKERNEL_INC_TARGETS kernel/include ./)
|
||||
if(EXISTS ${board_prefix}/include)
|
||||
list(APPEND VXKERNEL_INC_TARGETS ${board_prefix}/include)
|
||||
endif()
|
||||
|
||||
if(DEFINED VXKERNEL_CMAKE_DEBUG)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"dir list = ${VXKERNEL_DIR_TARGETS}\n"
|
||||
"src list = ${VXKERNEL_SRC_TARGETS}\n"
|
||||
"inc list = ${VXKERNEL_INC_TARGETS}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
#---
|
||||
# Compile-specific declaration
|
||||
#---
|
||||
|
||||
include_directories(${VXKERNEL_INC_TARGETS})
|
||||
add_compile_options(-Wall -Wextra ${VXSDK_BUILD_CFLAGS})
|
||||
add_library(vxkernel ${VXKERNEL_SRC_TARGETS})
|
||||
|
||||
#---
|
||||
# Install-specific declaration
|
||||
#---
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX ${VXSDK_PREFIX_INSTALL})
|
||||
install(TARGETS vxkernel DESTINATION "lib")
|
||||
install(DIRECTORY kernel/include/ DESTINATION "include")
|
||||
install(FILES ${VXKERNEL_INSTALL_EXTRA_FILES} DESTINATION "lib")
|
|
@ -1,19 +0,0 @@
|
|||
[meta]
|
||||
description = "Casio's Fxcg50 calculator board"
|
||||
author = 'Yann MAGNIN'
|
||||
|
||||
[config]
|
||||
modules = [
|
||||
'display',
|
||||
'fs',
|
||||
'hypervisor',
|
||||
'keyboard',
|
||||
'kmalloc',
|
||||
'timer',
|
||||
'rtc',
|
||||
'dma'
|
||||
]
|
||||
drivers = [
|
||||
'screen:R61524',
|
||||
'mpu:sh:sh7305'
|
||||
]
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
Linker script for the fxcg50 platform.
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf32-sh", "elf32-sh", "elf32-sh")
|
||||
OUTPUT_ARCH(sh4)
|
||||
ENTRY(_initialize)
|
||||
|
||||
/*
|
||||
** Linker script for user executables.
|
||||
*/
|
||||
MEMORY
|
||||
{
|
||||
/* virtual memory, read-write segment */
|
||||
userram (WX) : o = 0x00000000, l = 1M
|
||||
/* On-chip X memory */
|
||||
xram (rwx): o = 0xe5007000, l = 8k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
/* Code */
|
||||
.text : {
|
||||
PROVIDE(___kernel_reloc_start = .);
|
||||
|
||||
*(.text);
|
||||
*(.text.*);
|
||||
|
||||
_bctors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
_ectors = . ;
|
||||
|
||||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
_edtors = . ;
|
||||
|
||||
} > userram
|
||||
|
||||
/* vhex's interrupt handler blocks (.vhex.blocks)
|
||||
Although vhex's blocks end up in VBR space, they are relocated at
|
||||
startup by the library/drivers, so we store them here for now */
|
||||
.vhex.blocks : {
|
||||
KEEP(*(.vhex.blocks));
|
||||
} > userram
|
||||
|
||||
/* Exposed driver interfaces (.vhex.drivers)
|
||||
The driver information is required to start and configure the
|
||||
driver, even if the symbols are not referenced */
|
||||
.vhex.drivers : {
|
||||
_vhex_drivers_start = . ;
|
||||
KEEP(*(SORT_BY_NAME(.vhex.drivers.*)));
|
||||
_vhex_drivers_end = . ;
|
||||
} > userram
|
||||
|
||||
/* Exposed module interfaces (.vhex.modules) */
|
||||
.vhex.modules : {
|
||||
_vhex_modules_start = . ;
|
||||
KEEP(*(SORT_BY_NAME(.vhex.modules.*)));
|
||||
_vhex_modules_end = . ;
|
||||
} > userram
|
||||
|
||||
/* Exposed device interfaces (.vhex.device) */
|
||||
.vhex.device : {
|
||||
_vhex_devices_start = . ;
|
||||
KEEP(*(.vhex.device));
|
||||
_vhex_devices_end = . ;
|
||||
} > userram
|
||||
|
||||
/* Read-only sections */
|
||||
.rodata : {
|
||||
/* Read-Only data */
|
||||
*(.rodata);
|
||||
*(.rodata.*);
|
||||
} > userram
|
||||
|
||||
/* The GOT section is a bit exotic.
|
||||
When I generate the PIE executable, all global variables are stored
|
||||
is the *(.got) section and the __GLOBAL_OFFSET_TABLE__ object is
|
||||
generated and inserted in the *(.got.plt) section.
|
||||
|
||||
But, neither of *(.plt*) or *(rela*) section are generated to help
|
||||
me out for the "relocation" of each address stored in the GOT. The
|
||||
result is that the content of the GOT is always absolute addresses,
|
||||
which makes the machine crash.
|
||||
|
||||
So, the idea to bypass this limitation (probably due to the custom
|
||||
GCC which not provide critical information for the relocation) is to
|
||||
isolate the GOT in a standalone section and, after the loading of
|
||||
the image, walk thought the relocalised table and manually patch
|
||||
each address.
|
||||
|
||||
Moreover, the __GLOBAL_OFFSET_TABLE__ is used like a table, so, the
|
||||
section ".got.plt" SHOULD be before the .got section. */
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) *(.igot) }
|
||||
.got : { *(.got) }
|
||||
|
||||
.dynamic : { *(.dynamic) }
|
||||
|
||||
/* readable / writable data */
|
||||
.data ALIGN(4) : {
|
||||
|
||||
/* Data sections */
|
||||
*(.data);
|
||||
*(.data.*);
|
||||
*(COMMON);
|
||||
}
|
||||
|
||||
.bss ALIGN(4) : {
|
||||
/* bss section included to avoid missaligned segment */
|
||||
*(.bss);
|
||||
*(.bss.*);
|
||||
|
||||
/* dynamic BSS information (move me ?) */
|
||||
*(.dynbss)
|
||||
|
||||
/* Video RAM symbols
|
||||
|
||||
The Video RAM contains a full pixel sized frame for the
|
||||
screen. Its size is 396x224 and each pixel depth is 16bits,
|
||||
so (3996 * 224) * 2 = 177408
|
||||
|
||||
Thus, we use a double buffering methods to speed-up the
|
||||
render, this is why we define two VRAM */
|
||||
|
||||
_vhex_vram0 = ALIGN(32);
|
||||
. = _vhex_vram0 + 177408;
|
||||
_vhex_vram1 = ALIGN(32);
|
||||
. = _vhex_vram1 + 177408;
|
||||
|
||||
} > userram
|
||||
|
||||
/* Vhex VBR management geometry
|
||||
|
||||
Due to the SH3/SH4 VBR system, we have some restrictions:
|
||||
|
||||
The handlers should reside at VBR relative position, in P1 or P2
|
||||
protected space (0x8* or 0xA*). For our case, the bootloader will
|
||||
relocalise the kernel at the P1 area using the Casio's UTLB
|
||||
information. So, we don't have to wories about this here.
|
||||
|
||||
There are 3 vectors offset called by the processor : VBR + 0x100,
|
||||
0x400 and 0x600. The first offset is involved when an exception
|
||||
occur, the second when a memory (TLB) error is detected and the last
|
||||
when an interruptions is detected.
|
||||
|
||||
All exception and memory interuption will be handled "normaly" : a
|
||||
simple handler will be involved which will try to resolve/fix the
|
||||
problem, if nothing can be fixed, a panic screen will be displayed.
|
||||
|
||||
However, regarding interuptions, we will use a power-full and
|
||||
complexe technique to compact as most as possible the VBR size. To
|
||||
explain the technique we need to know that each interruption start at
|
||||
offset VBR + 0x600 and each interrupt source is "gapped" by 0x20 (
|
||||
for exemple the 0x400 is for TMU0, 0x420 is for TMU1 and 0x440 is for
|
||||
TMU2) Moreover, we have some "hole" in the "interrup map".
|
||||
So, the idea is to write all interrupt handler in "blocks" of 32
|
||||
bytes, and, for those that 32 bytes is to short, can use hole of the
|
||||
interrupt map. */
|
||||
.vhex.vbr : ALIGN(4) {
|
||||
|
||||
_vhex_vbr = . - 0x100;
|
||||
*(.vhex.exch.pretext) ;
|
||||
*(.vhex.exch)
|
||||
|
||||
. = _vhex_vbr + 0x400;
|
||||
*(.vhex.tlbh.pretext) ;
|
||||
*(.vhex.tlbh) ;
|
||||
|
||||
. = _vhex_vbr + 0x600;
|
||||
*(.vhex.inth.pretext) ;
|
||||
*(.vhex.inth) ;
|
||||
|
||||
/* interrupt block entry */
|
||||
__vhex_interrupth_start = ALIGN(4);
|
||||
|
||||
/* static ram start (for kmalloc) */
|
||||
PROVIDE(___sram_start = ALIGN(4) + 4096);
|
||||
|
||||
} > userram
|
||||
|
||||
/* On-chip memory sections: IL and Y (X is reserved) */
|
||||
|
||||
. = ORIGIN(xram);
|
||||
.xram ALIGN(4) : ALIGN(4) {
|
||||
|
||||
*(.vhex.xram)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > xram AT> userram
|
||||
|
||||
|
||||
|
||||
/* unwanted section */
|
||||
/DISCARD/ : {
|
||||
*(.rela.debug*)
|
||||
*(.gnu.*)
|
||||
*(.debug_info)
|
||||
*(.debug_abbrev)
|
||||
*(.debug_loc)
|
||||
*(.debug_aranges)
|
||||
*(.debug_ranges)
|
||||
*(.debug_line)
|
||||
*(.debug_str)
|
||||
*(.debug_*)
|
||||
*(.jcr)
|
||||
*(.eh_frame_hdr)
|
||||
*(.eh_frame)
|
||||
*(.comment)
|
||||
*(.interp)
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
//---
|
||||
// vhex:defs:attributes - Macros for compiler-specific attributes
|
||||
//---
|
||||
|
||||
#ifndef __VHEX_DEFS_ATTRIBUTES__
|
||||
# define __VHEX_DEFS_ATTRIBUTES__
|
||||
|
||||
/* Objects from specific sections */
|
||||
#define VSECTION(x) __attribute__((section(x)))
|
||||
|
||||
/* Unused parameters or variables */
|
||||
#define VUNUSED __attribute__((unused))
|
||||
/* Functions that *must* be inlined */
|
||||
#define VINLINE __attribute__((always_inline)) inline
|
||||
|
||||
/* Aligned variables */
|
||||
#define VALIGNED(x) __attribute__((aligned(x)))
|
||||
/* Packed structures. I require explicit alignment because if it's unspecified,
|
||||
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
|
||||
access sizes silently fail - honestly you don't want this to happen */
|
||||
#define VPACKED(x) __attribute__((packed, aligned(x)))
|
||||
/* Packed enumerations */
|
||||
#define VPACKEDENUM __attribute__((packed))
|
||||
/* Transparent unions */
|
||||
#define VTRANSPARENT __attribute__((transparent_union))
|
||||
|
||||
/* Weak symbols */
|
||||
#define VWEAK __attribute__((weak))
|
||||
|
||||
/* Constructors */
|
||||
#define VCONSTRUCTOR __attribute__((constructor))
|
||||
#define VDESTRUCTOR __attribute__((destructor))
|
||||
|
||||
/* Functions that do not return */
|
||||
#define VNORETURN __attribute__((noreturn))
|
||||
|
||||
#endif /*__VHEX_DEFS_ATTRIBUTES__*/
|
|
@ -1,184 +0,0 @@
|
|||
#include <vhex/keyboard/interface.h>
|
||||
#include <vhex/driver.h>
|
||||
#include <vhex/keyboard.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
//---
|
||||
// fake interrupt handler
|
||||
//---
|
||||
|
||||
#define __SDL2_KEYCODE_SUPPORTED 16
|
||||
|
||||
static struct {
|
||||
int sdl2_id;
|
||||
char const * const sdl2_name;
|
||||
int vhex_id;
|
||||
char const * const vhex_name;
|
||||
} key_translation[__SDL2_KEYCODE_SUPPORTED] = {
|
||||
{
|
||||
.sdl2_id = 49, .sdl2_name = "1",
|
||||
.vhex_id = VKEY_F1, .vhex_name = "F1"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 50, .sdl2_name = "2",
|
||||
.vhex_id = VKEY_F2, .vhex_name = "F2"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 51, .sdl2_name = "3",
|
||||
.vhex_id = VKEY_F3, .vhex_name = "F3"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 52, .sdl2_name = "4",
|
||||
.vhex_id = VKEY_F4, .vhex_name = "F4"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 53, .sdl2_name = "5",
|
||||
.vhex_id = VKEY_F5, .vhex_name = "F5"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 54, .sdl2_name = "6",
|
||||
.vhex_id = VKEY_F6, .vhex_name = "F6"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 0x4000004f, .sdl2_name = "right arrow",
|
||||
.vhex_id = VKEY_RIGHT, .vhex_name = "right arrow"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 0x40000050, .sdl2_name = "left arrow",
|
||||
.vhex_id = VKEY_LEFT, .vhex_name = "left arrow"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 0x40000051, .sdl2_name = "down arrow",
|
||||
.vhex_id = VKEY_DOWN, .vhex_name = "down arrow"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 0x40000052, .sdl2_name = "up arrow",
|
||||
.vhex_id = VKEY_UP, .vhex_name = "up arrow"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 13, .sdl2_name = "enter",
|
||||
.vhex_id = VKEY_EXE, .vhex_name = "EXE"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 0x400000e0, .sdl2_name = "shift",
|
||||
.vhex_id = VKEY_ALPHA, .vhex_name = "ALPHA"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 0x400000e1, .sdl2_name = "ctrl",
|
||||
.vhex_id = VKEY_SHIFT, .vhex_name = "SHIFT"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 27, .sdl2_name = "echap",
|
||||
.vhex_id = VKEY_EXIT, .vhex_name = "EXIT"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 8, .sdl2_name = "backspace",
|
||||
.vhex_id = VKEY_DEL, .vhex_name = "DEL"
|
||||
},
|
||||
{
|
||||
.sdl2_id = 9, .sdl2_name = "tab",
|
||||
.vhex_id = VKEY_MENU, .vhex_name = "MENU"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// keycache primitives
|
||||
//---
|
||||
|
||||
static int sdl_keycache_event_pop(vkey_event_t *event)
|
||||
{
|
||||
SDL_Event evt;
|
||||
int ret;
|
||||
|
||||
while (1)
|
||||
{
|
||||
ret = SDL_PollEvent(&evt);
|
||||
if (ret == 0) {
|
||||
if (event != NULL) {
|
||||
event->key = VKEY_NONE;
|
||||
event->type = VKEYEV_NONE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (evt.type == SDL_QUIT)
|
||||
exit(0);
|
||||
if (evt.type != SDL_KEYDOWN && evt.type != SDL_KEYUP)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < __SDL2_KEYCODE_SUPPORTED ; ++i) {
|
||||
if (evt.key.keysym.sym != key_translation[i].sdl2_id)
|
||||
continue;
|
||||
|
||||
if (event != NULL) {
|
||||
event->time = 0;
|
||||
event->key = key_translation[i].vhex_id;
|
||||
event->type = VKEYEV_DOWN;
|
||||
if (evt.type == SDL_KEYDOWN)
|
||||
event->type = VKEYEV_UP;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Internal fake driver definition
|
||||
//---
|
||||
|
||||
|
||||
/* __keysc_configure() : configure the keyboard module */
|
||||
static void __keysc_configure(void)
|
||||
{
|
||||
printf("[drv] keyboard: keymap notes:\n");
|
||||
printf(" +-------------+-------------+\n");
|
||||
printf(" | OS | Vhex |\n");
|
||||
printf(" +-------------+-------------+\n");
|
||||
for (int i = 0; i < __SDL2_KEYCODE_SUPPORTED; ++i) {
|
||||
printf(
|
||||
" | %-11s | %-11s |\n",
|
||||
key_translation[i].sdl2_name,
|
||||
key_translation[i].vhex_name
|
||||
);
|
||||
}
|
||||
printf(" +-------------+-------------+\n");
|
||||
}
|
||||
|
||||
/* __keysc_hsave() : save hardware information */
|
||||
static void __keysc_hsave(void)
|
||||
{
|
||||
// Nothing to do, this is a fake driver
|
||||
;
|
||||
}
|
||||
|
||||
/* __keysc_hrestore() : restore hadware information */
|
||||
static void __keysc_hrestore(void)
|
||||
{
|
||||
// Nothing to do, this is a fake driver
|
||||
;
|
||||
}
|
||||
|
||||
struct vhex_driver drv_keysc = {
|
||||
.name = "SDL2 Keyboard",
|
||||
.hsave = (void*)&__keysc_hsave,
|
||||
.hrestore = (void*)&__keysc_hrestore,
|
||||
.configure = (void*)&__keysc_configure,
|
||||
.state_size = 4,
|
||||
.flags = {
|
||||
.KEYBOARD = 1,
|
||||
.SHARED = 0,
|
||||
.UNUSED = 0,
|
||||
},
|
||||
.module_data = &(struct keyboard_drv_interface){
|
||||
.keycache_init = NULL,
|
||||
.keycache_keydown = NULL,
|
||||
.keycache_event_wait = NULL,
|
||||
.keycache_event_pop = &sdl_keycache_event_pop,
|
||||
.keycache_event_push = NULL,
|
||||
.keycache_quit = NULL,
|
||||
}
|
||||
};
|
||||
VHEX_DECLARE_DRIVER(03, drv_keysc);
|
|
@ -0,0 +1,49 @@
|
|||
"""
|
||||
pylint - checker for vxnorm
|
||||
|
||||
This file does not expose an explicite VxChecker object declaration to avoid
|
||||
dependencies handling, you just need to provide:
|
||||
======================= ===============================================
|
||||
parse_file() Parse the source file
|
||||
======================= ===============================================
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def parse_file(checker, _, pathinfo):
|
||||
""" parse the mapped file
|
||||
|
||||
The file is mapped using mmap() and seeked through offset 0 to avoid too
|
||||
many I/O operations with classical file primitive.
|
||||
|
||||
@args
|
||||
> checker (VxChecker) - current checker instance for this file
|
||||
> mfile (mmap) - mmap instance of the file, seeked at 0
|
||||
> pathname (str) - file pathname
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
status = subprocess.run(
|
||||
['pylint', pathinfo['filepath']], capture_output=True, check=False
|
||||
)
|
||||
if status.returncode == 0:
|
||||
return
|
||||
|
||||
for line in status.stdout.decode('utf8').split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
if line[0] == '*':
|
||||
continue
|
||||
if line[0] == '-':
|
||||
break
|
||||
if not (line := line.split(' ', 2)):
|
||||
continue
|
||||
checker.notify(
|
||||
line[0].split(':', 1)[1][:-1],
|
||||
f"[{line[1][:-1]}] {line[2]}",
|
||||
'pylint'
|
||||
)
|
|
@ -0,0 +1,58 @@
|
|||
" Force-display trailing whitespaces
|
||||
highlight ExtraWhitespace ctermbg=red guibg=red
|
||||
match ExtraWhitespace /\s\+$/
|
||||
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
|
||||
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
|
||||
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
|
||||
autocmd BufWinLeave * call clearmatches()
|
||||
|
||||
" indicate the compiler to use
|
||||
let g:ale_asm_gcc_executable = 'sh-elf-vhex-gcc'
|
||||
let g:ale_c_gcc_executable = 'sh-elf-vhex-gcc'
|
||||
let g:ale_c_cc_executable = 'sh-elf-vhex-gcc'
|
||||
let g:ale_cpp_gcc_executable = 'sh-elf-vhex-gcc'
|
||||
|
||||
" Find GiteaPC include directory information
|
||||
let s:_hdrflags = [
|
||||
\ '-I./vxgos/bootloader/include',
|
||||
\ '-I./vxgos/bootloader/boards/fxcg50/include/',
|
||||
\ ]
|
||||
let s:_cflags = [
|
||||
\ '-DFXCG50',
|
||||
\ '-std=c11',
|
||||
\ '-Wall',
|
||||
\ '-Wextra',
|
||||
\ ]
|
||||
let s:_asmflags = [
|
||||
\ '-DFXCG50',
|
||||
\ '-x assembler-with-cpp',
|
||||
\ '-Wall',
|
||||
\ '-Wextra',
|
||||
\ '-m4-nofpu',
|
||||
\ '-mb',
|
||||
\ '-Wa,--dsp',
|
||||
\ ]
|
||||
|
||||
" convert table into string
|
||||
let s:_hdrflags = join(s:_hdrflags)
|
||||
let s:_asmflags = join(s:_asmflags)
|
||||
let s:_cflags = join(s:_cflags)
|
||||
|
||||
" Patch option
|
||||
" @note
|
||||
" - we setup cpp because header file is considered ad C++ file
|
||||
" - we force sh-elf-vhex for assembly file only
|
||||
let g:ale_asm_gcc_options = s:_asmflags.' '.s:_hdrflags
|
||||
let g:ale_c_gcc_options = s:_cflags.' '.s:_hdrflags
|
||||
let g:ale_c_cc_options = s:_cflags.' '.s:_hdrflags
|
||||
let g:ale_cpp_cc_options = s:_cflags.' '.s:_hdrflags
|
||||
let g:ale_cpp_gcc_options = s:_cflags.' '.s:_hdrflags
|
||||
|
||||
" Enable completion
|
||||
let g:ale_completion_enabled = 1
|
||||
|
||||
" Enable linter when enter is pressed
|
||||
let g:ale_lint_on_enter = 1
|
||||
|
||||
" Force display non-printable chars
|
||||
set list
|
|
@ -0,0 +1,30 @@
|
|||
"""
|
||||
patch kernel image for the Raspberry Pi Imager
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print("./script <kernel_original> <output>")
|
||||
|
||||
if os.path.exists(sys.argv[2]):
|
||||
yes = input("output file already exists, overwrite? [Yn]")
|
||||
if yes and yes not in ['y', 'Y', 'yes']:
|
||||
print("output file already exists, abord", file=sys.stderr)
|
||||
sys.exit(84)
|
||||
os.remove(sys.argv[2])
|
||||
|
||||
with open(sys.argv[2], "xb") as patch:
|
||||
with open(sys.argv[1], "rb") as kernel:
|
||||
content = kernel.read()
|
||||
patch.write(content)
|
||||
kernel_size = len(content)
|
||||
if kernel_size % 512:
|
||||
for _ in range(0, 512 - (kernel_size % 512)):
|
||||
patch.write(0x00.to_bytes(1))
|
||||
|
||||
print(f"{sys.argv[2]}: patched and generated")
|
|
@ -0,0 +1,2 @@
|
|||
#TODO : check vxdev virtual env
|
||||
#TODO : check if vxSDK is installed
|
|
@ -1,15 +1,19 @@
|
|||
#! /usr/bin/env python3
|
||||
"""
|
||||
Vhex kernel developement script
|
||||
"""
|
||||
import sys
|
||||
|
||||
import cli
|
||||
from core.logger import log
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
__HELP__ = """ usage: vxdev [ACTION] ...
|
||||
|
||||
Actions:
|
||||
configure perform configuration step
|
||||
doctor display information about the vxOS and supported feature
|
||||
build perfrom build step
|
||||
install perform installation step
|
||||
uninstall perform uninstallation step
|
||||
|
@ -20,27 +24,33 @@ Options:
|
|||
You can try `vxdev <action> --help` for more information about each action
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def _main(argv):
|
||||
""" main entry of the script """
|
||||
if '-h' in argv or '--help' in argv:
|
||||
print(__HELP__)
|
||||
log.user(__HELP__)
|
||||
sys.exit(0)
|
||||
if not argv:
|
||||
print(__HELP__)
|
||||
log.user(__HELP__)
|
||||
sys.exit(84)
|
||||
if argv[0] == 'configure':
|
||||
sys.exit(cli.configure(argv[1:]))
|
||||
|
||||
if argv[0] in ['-v', '-vv', '-vvv']:
|
||||
log.level += len(argv[0]) - 1
|
||||
argv = argv[1:]
|
||||
if not argv:
|
||||
log.error(__HELP__)
|
||||
sys.exit(84)
|
||||
|
||||
if argv[0] == 'build':
|
||||
sys.exit(cli.build(argv[1:]))
|
||||
if argv[0] == 'install':
|
||||
sys.exit(cli.install(argv[1:]))
|
||||
if argv[0] == 'uninstall':
|
||||
sys.exit(cli.uninstall(argv[1:]))
|
||||
if argv[0] == 'unitests':
|
||||
sys.exit(cli.unitests(argv[1:]))
|
||||
|
||||
print(f"unrecognized argument '{argv[0]}'", file=sys.stderr)
|
||||
sys.exit(84)
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
sys.exit(_main(sys.argv[1:]))
|
||||
|
|
|
@ -1,34 +1,22 @@
|
|||
"""
|
||||
Expose configure script part
|
||||
"""
|
||||
from cli.configure import configure_entry
|
||||
from cli.build import build_entry
|
||||
from cli.install import install_entry
|
||||
from cli.uninstall import uninstall_entry
|
||||
from cli.unitests import unitests_entry
|
||||
|
||||
__all__ = [
|
||||
'configure',
|
||||
'build',
|
||||
'install',
|
||||
'uninstall',
|
||||
'unitests',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def configure(argv):
|
||||
""" invoke configuration script """
|
||||
return configure_entry(argv)
|
||||
|
||||
def build(argv):
|
||||
""" invoke build script """
|
||||
return build_entry(argv)
|
||||
|
||||
def install(argv):
|
||||
""" invoke install script """
|
||||
return install_entry(argv)
|
||||
|
||||
def uninstall(argv):
|
||||
""" invoke uninstall script """
|
||||
return uninstall_entry(argv)
|
||||
def unitests(argv):
|
||||
""" invoke unitests script """
|
||||
return unitests_entry(argv)
|
||||
|
|
|
@ -4,12 +4,17 @@ Vhex kernel build script
|
|||
import os
|
||||
import sys
|
||||
|
||||
from core.cmake import cmake_build
|
||||
from core.logger import log
|
||||
from core.build import VxOSBuild
|
||||
|
||||
__all__ = [
|
||||
'build_entry'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internal
|
||||
#---
|
||||
|
||||
__HELP__ = """ usage: vxdev build [OPTIONS] ...
|
||||
|
||||
Build script for the Vhex kernel
|
||||
|
@ -26,22 +31,22 @@ Options:
|
|||
def build_entry(argv):
|
||||
""" Build entry """
|
||||
if '-h' in argv or '--help' in argv:
|
||||
print(__HELP__)
|
||||
log.user(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
if 'VXSDK_PKG_TARGET' not in os.environ \
|
||||
or 'VXSDK_PREFIX_BUILD' not in os.environ \
|
||||
or 'VXSDK_PREFIX_INSTALL' not in os.environ:
|
||||
log.error("unable to build the vxGOS without using the vxSDK")
|
||||
|
||||
enable_verbose = False
|
||||
for arg in argv:
|
||||
if arg in ['-v', '--verbose']:
|
||||
enable_verbose = True
|
||||
continue
|
||||
print(f"unrecognized argument '{arg}'", file=sys.stderr)
|
||||
sys.exit(84)
|
||||
log.error(f"unrecognized argument '{arg}'")
|
||||
|
||||
if 'VXSDK_PREFIX_BUILD' not in os.environ:
|
||||
print(
|
||||
"unable to generate the configuration file, you should use the "
|
||||
"vxSDK",
|
||||
file=sys.stderr
|
||||
)
|
||||
sys.exit(84)
|
||||
return cmake_build(os.environ['VXSDK_PREFIX_BUILD'], enable_verbose)
|
||||
build = VxOSBuild(enable_verbose)
|
||||
build.configure()
|
||||
build.build()
|
||||
return 0
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
"""
|
||||
Vhex kernel configuration script
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from core.board import board_command, board_select
|
||||
from core.format import format_command, format_select
|
||||
from core.config_file import config_file_generate
|
||||
from core.cmake import cmake_configure
|
||||
|
||||
__all__ = [
|
||||
'configure_entry'
|
||||
]
|
||||
|
||||
__HELP__ = """ usage: vxdev configure [OTPIONS ...] [ACTIONS ...]
|
||||
|
||||
Configuration script for the Vhex unikernel.
|
||||
|
||||
Actions:
|
||||
board: Board hadling
|
||||
<board-name> select the board as compilation target
|
||||
--list,list display board list information
|
||||
--info,info display a particular board information
|
||||
|
||||
vdso: "Fake" kernel compilation like a vDSO
|
||||
--info,info display all infromation that the vdso will exposes
|
||||
--generate,gen generate the fake build source (debug)
|
||||
|
||||
Options:
|
||||
-h,--help display this message
|
||||
--board=<board> select the board to target
|
||||
--format=<target>
|
||||
select the format of the library generation. You can use two format:
|
||||
<> static - generate a static library
|
||||
<> vdso - generate a "fake" dynamic library (default)
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def configure_entry(argv):
|
||||
""" main entry of the script """
|
||||
# early check
|
||||
if '-h' in argv or '--help' in argv:
|
||||
print(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
# handle vxSDK configuration
|
||||
if 'VXSDK_PKG_TARGET' in os.environ:
|
||||
argv.append(f"--board={os.environ['VXSDK_PKG_TARGET']}")
|
||||
|
||||
# default behaviour
|
||||
if not argv:
|
||||
print(__HELP__)
|
||||
sys.exit(84)
|
||||
|
||||
# configure default value
|
||||
kernconf = {
|
||||
'VXKERNEL_ENABLE_VERBOSE' : False,
|
||||
'VXKERNEL_ENABLE_MODULES' : [],
|
||||
'VXKERNEL_ENABLE_DRIVERS' : [],
|
||||
'VXKERNEL_TARGET_FORMAT' : 'vdso',
|
||||
}
|
||||
|
||||
# check subcommand
|
||||
if argv[0] == 'board':
|
||||
sys.exit(board_command(argv[1:]))
|
||||
if argv[0] == 'format':
|
||||
sys.exit(format_command(argv[1:]))
|
||||
|
||||
# check user arguments
|
||||
for arg in argv:
|
||||
if arg.find('--board=') == 0:
|
||||
board_select(kernconf, arg[8:])
|
||||
elif arg.find('--format=') == 0:
|
||||
format_select(kernconf, arg[9:])
|
||||
else:
|
||||
print(f"argument '{arg}' not recognized", file=sys.stderr)
|
||||
sys.exit(84)
|
||||
|
||||
# check vxSDK validity
|
||||
try:
|
||||
prefix_src = os.environ['VXSDK_CURRENT_SOURCE_DIR']
|
||||
prefix_build = os.environ['VXSDK_PREFIX_BUILD']
|
||||
if config_file_generate(kernconf, prefix_build) == 0:
|
||||
sys.exit(cmake_configure(prefix_build, prefix_src))
|
||||
except KeyError:
|
||||
print(
|
||||
"unable to generate the configuration file, you should use the "
|
||||
"vxSDK",
|
||||
file=sys.stderr
|
||||
)
|
||||
sys.exit(84)
|
|
@ -1,50 +0,0 @@
|
|||
"""
|
||||
Vhex kernel install script
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from core.cmake import cmake_install
|
||||
|
||||
__all__ = [
|
||||
'install_entry'
|
||||
]
|
||||
|
||||
__HELP__ = """ usage: vxdev install [OPTIONS] ...
|
||||
|
||||
Install script for the Vhex kernel
|
||||
|
||||
Options:
|
||||
-h, --help display this message
|
||||
-v, --verbose display more information during the building process
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def install_entry(argv):
|
||||
""" Install entry """
|
||||
if '-h' in argv or '--help' in argv:
|
||||
print(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
enable_verbose = False
|
||||
for arg in argv:
|
||||
if arg in ['-v', '--verbose']:
|
||||
enable_verbose = True
|
||||
continue
|
||||
print(f"unrecognized argument '{arg}'", file=sys.stderr)
|
||||
sys.exit(84)
|
||||
|
||||
try:
|
||||
_ = os.environ['VXSDK_PREFIX_BUILD']
|
||||
__ = os.environ['VXSDK_PREFIX_INSTALL']
|
||||
except KeyError:
|
||||
print(
|
||||
"unable to generate the configuration file, you should use the "
|
||||
"vxSDK",
|
||||
file=sys.stderr
|
||||
)
|
||||
sys.exit(84)
|
||||
return cmake_install(os.environ['VXSDK_PREFIX_BUILD'], enable_verbose)
|
|
@ -1,50 +0,0 @@
|
|||
"""
|
||||
Vhex kernel uninstall script
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from core.cmake import cmake_uninstall
|
||||
|
||||
__all__ = [
|
||||
'uninstall_entry'
|
||||
]
|
||||
|
||||
__HELP__ = """ usage: vxdev uninstall [OPTIONS] ...
|
||||
|
||||
uninstall script for the Vhex kernel
|
||||
|
||||
Options:
|
||||
-h, --help display this message
|
||||
-v, --verbose display more information during the building process
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def uninstall_entry(argv):
|
||||
""" uninstall entry """
|
||||
if '-h' in argv or '--help' in argv:
|
||||
print(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
enable_verbose = False
|
||||
for arg in argv:
|
||||
if arg in ['-v', '--verbose']:
|
||||
enable_verbose = True
|
||||
continue
|
||||
print(f"unrecognized argument '{arg}'", file=sys.stderr)
|
||||
sys.exit(84)
|
||||
|
||||
try:
|
||||
_ = os.environ['VXSDK_PREFIX_BUILD']
|
||||
__ = os.environ['VXSDK_PREFIX_INSTALL']
|
||||
except KeyError:
|
||||
print(
|
||||
"unable to generate the configuration file, you should use the "
|
||||
"vxSDK",
|
||||
file=sys.stderr
|
||||
)
|
||||
sys.exit(84)
|
||||
return cmake_uninstall(os.environ['VXSDK_PREFIX_BUILD'], enable_verbose)
|
|
@ -0,0 +1,46 @@
|
|||
"""
|
||||
cli.unitests - unit tests interface
|
||||
"""
|
||||
import sys
|
||||
|
||||
from core.logger import log
|
||||
from core.unitests import VxOSUnitest
|
||||
|
||||
__all__ = [
|
||||
'unitests_entry'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internal
|
||||
#---
|
||||
|
||||
__HELP__ = """ usage: vxdev unitest [OPTIONS] ...
|
||||
|
||||
Build script for the Vhex kernel
|
||||
|
||||
Options:
|
||||
-h, --help display this message
|
||||
-v, --verbose display more information during the building process
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def unitests_entry(argv):
|
||||
""" Build entry """
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
enable_verbose = False
|
||||
for arg in argv:
|
||||
if arg in ['-v', '--verbose']:
|
||||
enable_verbose = True
|
||||
continue
|
||||
log.error(f"unrecognized argument '{arg}'")
|
||||
|
||||
unitest = VxOSUnitest(enable_verbose)
|
||||
unitest.configure()
|
||||
unitest.execute()
|
||||
return 0
|
|
@ -1,242 +0,0 @@
|
|||
"""
|
||||
Board handling
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import toml
|
||||
|
||||
__all__ = [
|
||||
'board_select',
|
||||
'board_command',
|
||||
]
|
||||
|
||||
#---
|
||||
# Internal functions
|
||||
#---
|
||||
|
||||
__PROJ_PREFIX__ = f"{os.path.dirname(__file__)}/../../.."
|
||||
|
||||
def _warning(text):
|
||||
print(text, file=sys.stderr)
|
||||
|
||||
def _board_fetch_info_meta(board_info, board_desc, board):
|
||||
""" Fetch and check board meta information
|
||||
|
||||
@args
|
||||
> board_info (dict) - board information
|
||||
> board_desc (dict) - board TOML parsed file
|
||||
> board (str) - board name
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
if 'meta' not in board_desc:
|
||||
_warning(f"board '{board}' : missing meta section")
|
||||
return -1
|
||||
if 'author' not in board_desc['meta']:
|
||||
_warning(f"board '{board}' : missing author names")
|
||||
return -2
|
||||
if 'description' not in board_desc['meta']:
|
||||
_warning(f"board '{board}' : missing description")
|
||||
return -3
|
||||
board_info['author'] = board_desc['meta']['author']
|
||||
board_info['desc'] = board_desc['meta']['description']
|
||||
return 0
|
||||
|
||||
def _board_fetch_info_config(board_info, board_desc, board):
|
||||
""" Fetch and check board configuration information
|
||||
|
||||
@args
|
||||
> board_info (dict) - board information
|
||||
> board_desc (dict) - board TOML parsed file
|
||||
> board (str) - board name
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
if 'config' not in board_desc:
|
||||
_warning(f"board '{board}' : missing config section")
|
||||
return -1
|
||||
board_conf = board_desc['config']
|
||||
if 'modules' not in board_conf:
|
||||
_warning(f"board '{board}' : missing modules declaration")
|
||||
return -2
|
||||
if 'drivers' not in board_conf:
|
||||
_warning(f"board '{board}' : missing drivers declaration")
|
||||
return -3
|
||||
mod_prefix = f"{__PROJ_PREFIX__}/kernel/src/modules"
|
||||
for mod in board_conf['modules']:
|
||||
if not os.path.exists(f"{mod_prefix}/{mod}"):
|
||||
_warning(f"{board} : module '{mod}' does not exists, skipped")
|
||||
continue
|
||||
board_info['config']['modules'].append(mod)
|
||||
drv_prefix = f"{__PROJ_PREFIX__}/kernel/src/drivers"
|
||||
for drv in board_conf['drivers']:
|
||||
drv_path = drv.replace(':', '/')
|
||||
if not os.path.exists(f"{drv_prefix}/{drv_path}"):
|
||||
_warning(f"{board} : driver '{drv}' does not exists, skipped")
|
||||
continue
|
||||
board_info['config']['drivers'].append(drv_path)
|
||||
if 'install_files' in board_conf:
|
||||
for file in board_conf['install_files']:
|
||||
file_pathname = f"{__PROJ_PREFIX__}/boards/{board}/{file}"
|
||||
if not os.path.exists(file_pathname):
|
||||
_warning("{board}: unable to find the file '{file}', skipped")
|
||||
continue
|
||||
board_info['config']['install_files'].append(file_pathname)
|
||||
return 0
|
||||
|
||||
def _board_fetch_info(board):
|
||||
""" Generate all information about a particular board
|
||||
|
||||
@return
|
||||
> a dictionary with all board information : {
|
||||
'name' : <board name>,
|
||||
'path' : <board absolute path>,
|
||||
'config' : {
|
||||
'drivers' : <list of driver pseudo-pathname>,
|
||||
'modules' : <list of module pseudo-pathname>
|
||||
},
|
||||
}
|
||||
"""
|
||||
board_info = {
|
||||
'name' : board,
|
||||
'path' : f"{__PROJ_PREFIX__}/boards/{board}",
|
||||
'author' : 'unknown',
|
||||
'desc' : 'unknown',
|
||||
'config' : {
|
||||
'drivers' : [],
|
||||
'modules' : [],
|
||||
'install_files' : []
|
||||
}
|
||||
}
|
||||
|
||||
# Check board existance
|
||||
if not os.path.exists(board_info['path']):
|
||||
_warning(f"board '{board}' does not exists, skipped")
|
||||
return {}
|
||||
board_desc_path = f"{board_info['path']}/board.toml"
|
||||
if not os.path.exists(board_desc_path):
|
||||
_warning(f"board '{board}' : missing board description, skipped")
|
||||
return {}
|
||||
|
||||
# Try to dump (and tranforms) board information
|
||||
with open(board_desc_path, "r", encoding='utf8') as file:
|
||||
board_desc = toml.loads(file.read())
|
||||
|
||||
# check meta board information
|
||||
if _board_fetch_info_meta(board_info, board_desc, board) != 0:
|
||||
return {}
|
||||
|
||||
# check board configuration
|
||||
if _board_fetch_info_config(board_info, board_desc, board) != 0:
|
||||
return {}
|
||||
|
||||
# return the board information
|
||||
return board_info
|
||||
|
||||
def _board_fetch_available_board():
|
||||
""" Fetch all available board
|
||||
|
||||
@return
|
||||
> a list of all available board name
|
||||
"""
|
||||
# fetch folder information
|
||||
board_prefix = f"{__PROJ_PREFIX__}/boards"
|
||||
if not (available_list := list(os.walk(board_prefix))):
|
||||
return []
|
||||
# fetch folder content only
|
||||
if not (available_list := available_list[0]):
|
||||
return 0
|
||||
# fetch directory only
|
||||
return available_list[1]
|
||||
|
||||
def _board_generate_conf(kernconf, board_info):
|
||||
""" generate exportable variable information
|
||||
|
||||
@args
|
||||
> kernconf (dict) - kernel configuration
|
||||
> board_info (dict) - board information
|
||||
|
||||
@return
|
||||
> nothing
|
||||
"""
|
||||
bconf = board_info['config']
|
||||
kernconf['VXKERNEL_ENABLE_BOARD'] = board_info['name']
|
||||
kernconf['VXKERNEL_ENABLE_MODULES'] = bconf['modules']
|
||||
kernconf['VXKERNEL_ENABLE_DRIVERS'] = bconf['drivers']
|
||||
kernconf['VXKERNEL_INSTALL_EXTRA_FILES'] = bconf['install_files']
|
||||
|
||||
def _board_display_list(verbose, board_target):
|
||||
""" Display board information
|
||||
|
||||
@args
|
||||
> verbose (bool) - display more information
|
||||
> board_target (str) - targeted board
|
||||
|
||||
@return
|
||||
> 0 if success, negative value othervise
|
||||
"""
|
||||
board_list = [board_target]
|
||||
if not board_target:
|
||||
board_list = _board_fetch_available_board()
|
||||
|
||||
print(f"\033[1m{'board':<16}{'Authors':<16}Description\033[0m")
|
||||
for board in board_list:
|
||||
if not (board_info := _board_fetch_info(board)):
|
||||
continue
|
||||
content = f"{board:<16}{board_info['author']:<16}{board_info['desc']}"
|
||||
if verbose:
|
||||
fake_kernconf = {}
|
||||
_board_generate_conf(fake_kernconf, board_info)
|
||||
content += ":\n"
|
||||
for item in fake_kernconf.items():
|
||||
content += f" {item[0]:<16} = {item[1]}\n"
|
||||
print(content)
|
||||
|
||||
return 0
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def board_select(kernconf, board):
|
||||
""" Select a board and generate output information
|
||||
|
||||
@args
|
||||
> kernconf (dict) - kernel configuration information
|
||||
> board (str) - argument for the selection
|
||||
|
||||
@return
|
||||
> Nothing, hangout if error. (return are only here for syntax sugar)
|
||||
"""
|
||||
if not (board_info := _board_fetch_info(board)):
|
||||
sys.exit(84)
|
||||
return _board_generate_conf(kernconf, board_info)
|
||||
|
||||
def board_command(argv):
|
||||
""" Board-specific handler
|
||||
|
||||
@arg
|
||||
> argv (list,str) - subcommand arguments
|
||||
|
||||
@return
|
||||
> Never
|
||||
"""
|
||||
if not argv:
|
||||
_warning('missing board argument')
|
||||
sys.exit(84)
|
||||
|
||||
board = ''
|
||||
verbose = False
|
||||
for arg in argv:
|
||||
if arg in ['--list', 'list']:
|
||||
verbose = False
|
||||
elif arg in ['--info', 'info']:
|
||||
verbose = True
|
||||
else:
|
||||
if board:
|
||||
_warning(f"{board} already selected, change for '{arg}'")
|
||||
board = arg
|
||||
|
||||
sys.exit(_board_display_list(verbose, board))
|
|
@ -0,0 +1,140 @@
|
|||
"""
|
||||
core.build - build abstraction
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from core.logger import log
|
||||
from core.build.bootloader import bootloader_configure
|
||||
from core.build.kernel import kernel_configure
|
||||
from core.build.cmake import cmake_build_compile
|
||||
from core.build.aslr import aslr_generate
|
||||
|
||||
from version import (
|
||||
# VXGOS_OS_VERSION,
|
||||
VXGOS_KERNEL_VERSION,
|
||||
VXGOS_BOOTLOADER_VERSION,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'VxOSBuild',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
class VxOSBuild():
|
||||
""" performs build abstraction
|
||||
|
||||
The build system of vxGOS is particular
|
||||
TODO
|
||||
"""
|
||||
def __new__(cls, *_, **__):
|
||||
""" try to acquire boards required file and load external post-scripts
|
||||
"""
|
||||
obj = super().__new__(cls)
|
||||
obj._target = os.environ['VXSDK_PKG_TARGET']
|
||||
obj._prefix_base = f"{os.path.dirname(__file__)}/../../../../vxgos/"
|
||||
obj._prefix_base = os.path.normpath(obj._prefix_base)
|
||||
try:
|
||||
sys.path.append(f"{obj._prefix_base}/boards/{obj._target}/")
|
||||
mod = __import__(
|
||||
'generate',
|
||||
fromlist=[
|
||||
'generate_aslr_blob',
|
||||
'generate_image'
|
||||
]
|
||||
)
|
||||
error_base_str = f"{obj._target} : `generate.py` do not exposes"
|
||||
if not hasattr(mod, 'generate_aslr_blob'):
|
||||
log.emergency(f"{error_base_str} : `generate_aslr_blob()`")
|
||||
if not hasattr(mod, 'generate_image'):
|
||||
log.emergency(f"{error_base_str} : `generate_image()`")
|
||||
obj._postscript = {
|
||||
'alsr' : mod.generate_aslr_blob,
|
||||
'image' : mod.generate_image
|
||||
}
|
||||
sys.path.pop()
|
||||
except ImportError as _:
|
||||
log.error(f"unable to aquire '{obj._target}' post-script")
|
||||
return obj
|
||||
|
||||
def __init__(self, verbose=False):
|
||||
""" create build abstraction object """
|
||||
self._verbose = verbose
|
||||
self._prefix = {
|
||||
'build' : os.environ['VXSDK_PREFIX_BUILD'],
|
||||
'bootloader' : f"{self._prefix_base}/bootloader",
|
||||
#'kernel' : f"{self._prefix_base}/kernel",
|
||||
}
|
||||
self._binlist = {
|
||||
'bootloader' : '',
|
||||
#'kernel' : '',
|
||||
#'os' : ''
|
||||
}
|
||||
|
||||
#---
|
||||
# Attributes
|
||||
#---
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
""" return the prefix dictionary """
|
||||
return self._prefix
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
""" return the selected target """
|
||||
return self._target
|
||||
|
||||
@property
|
||||
def verbose(self):
|
||||
""" return if the verbose mode is selected """
|
||||
return self._verbose
|
||||
|
||||
@property
|
||||
def postscript(self):
|
||||
""" return if the verbose mode is selected """
|
||||
return self._postscript
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def configure(self):
|
||||
""" compile OS/kernel, bootloader and perform post-build scripts """
|
||||
self._binlist = {
|
||||
'bootloader': bootloader_configure(self, VXGOS_BOOTLOADER_VERSION),
|
||||
#'kernel' : kernel_configure(self, VXGOS_KERNEL_VERSION),
|
||||
#'os' : '',
|
||||
}
|
||||
|
||||
def build(self):
|
||||
""" compile OS/kernel, bootloader and perform post-build scripts
|
||||
|
||||
TODO
|
||||
"""
|
||||
binpathlist = {}
|
||||
log.user("[+] compiling each OS part...")
|
||||
for partname in self._binlist:
|
||||
log.user(f"- {partname}...")
|
||||
binpathlist[partname] = cmake_build_compile(
|
||||
partname,
|
||||
f"{self.prefix['build']}/{partname}/",
|
||||
self.verbose
|
||||
)
|
||||
|
||||
blobpathlist = {}
|
||||
log.user("[+] generate ASLR information...")
|
||||
for bininfo in binpathlist.items():
|
||||
log.user(f"- {bininfo[0]}...")
|
||||
blobpathlist[bininfo[0]] = aslr_generate(self, bininfo[1])
|
||||
|
||||
return self.postscript['image'](
|
||||
self.prefix['build'],
|
||||
blobpathlist['bootloader'],
|
||||
'',
|
||||
#blobpathlist['kernel'],
|
||||
#blobpathlist['os']
|
||||
)
|
|
@ -0,0 +1,64 @@
|
|||
"""
|
||||
core.aslr - generate ASLR symbols table
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'aslr_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _fetch_symtab(binpath):
|
||||
"""
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
f"readelf -r {binpath}".split(),
|
||||
capture_output=True,
|
||||
check=False
|
||||
)
|
||||
if ret.returncode != 0:
|
||||
log.error(ret.stderr.decode('utf8'))
|
||||
symtab = []
|
||||
for i, line in enumerate(ret.stdout.decode('utf8').splitlines()):
|
||||
if i < 3:
|
||||
continue
|
||||
if len(sinfo := line.split()) != 7:
|
||||
continue
|
||||
log.debug(f"[{i}] {sinfo}")
|
||||
symtab.append(sinfo)
|
||||
return symtab
|
||||
|
||||
def _fetch_sectab(binpath):
|
||||
"""
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
f"readelf --sections --wide {binpath}".split(),
|
||||
capture_output=True,
|
||||
check=False
|
||||
)
|
||||
if ret.returncode != 0:
|
||||
log.error(ret.stderr.decode('utf8'))
|
||||
sectab = []
|
||||
for i, line in enumerate(ret.stdout.decode('utf8').splitlines()):
|
||||
if line.find(' [') != 0:
|
||||
continue
|
||||
secinfo = line[7:].split()
|
||||
log.debug(f"[{i}] {secinfo[0]}")
|
||||
sectab.append(secinfo)
|
||||
return sectab
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def aslr_generate(build, binpath):
|
||||
""" generate ASLR symbols table
|
||||
"""
|
||||
symtab = _fetch_symtab(binpath)
|
||||
sectab = _fetch_sectab(binpath)
|
||||
return build.postscript['alsr'](binpath, symtab, sectab)
|
|
@ -0,0 +1,89 @@
|
|||
"""
|
||||
core.build.bootloader - bootloader build abstraction
|
||||
"""
|
||||
import subprocess
|
||||
import glob
|
||||
|
||||
from core.logger import log
|
||||
from core.build.cmake import cmake_build_configure
|
||||
from core.build.compiles import compiles_fetch
|
||||
|
||||
__all__ = [
|
||||
'bootloader_configure',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def bootloader_configure(build, version):
|
||||
""" Configure the bootloader
|
||||
|
||||
The configuration step is special. It will first fetch all common source
|
||||
files (stored at <vxgos/bootloader/src/>) then, fetch board-specific files
|
||||
like extra source file and the linker script.
|
||||
|
||||
After theses operation, a CMake file will be generated in the build prefix
|
||||
(<env['VXSDK_BUILD_PREFIX']/bootloader/>)
|
||||
"""
|
||||
prefix = build.prefix['bootloader']
|
||||
log.debug('fetching bootloader files...')
|
||||
log.debug(f"> prefix = {prefix}")
|
||||
|
||||
log.debug('- fetching compiles informations...')
|
||||
compiles = compiles_fetch(f"{prefix}/boards/{build.target}/compiles.toml")
|
||||
if not compiles:
|
||||
log.emergency(
|
||||
"unable to find bootloader's compilation information "
|
||||
"(compiles.toml)"
|
||||
)
|
||||
|
||||
assets_src = []
|
||||
if compiles.assets:
|
||||
log.debug('- generate assets sources files...')
|
||||
cmd = '/usr/bin/env python3 '
|
||||
cmd += f"{prefix}/../../sdk/converter conv-assets "
|
||||
cmd += f"{prefix}/assets "
|
||||
cmd += f"{prefix}/../../.vxsdk/{build.target}/converter/bootloader "
|
||||
cmd += f"--filter={','.join(compiles.assets)} "
|
||||
cmd += '--bootloader '
|
||||
if compiles.toolchain_endian == 'little':
|
||||
cmd += '--little-endian'
|
||||
else:
|
||||
cmd += '--big-endian'
|
||||
proc = subprocess.run(cmd.split(), capture_output=True, check=False)
|
||||
if proc.returncode != 0:
|
||||
log.emergency(
|
||||
f"unable to convert bootloader assets : {proc.stderr}"
|
||||
)
|
||||
assets_src = proc.stdout.decode('utf8').split()
|
||||
log.debug(f"> assets src = {assets_src}")
|
||||
|
||||
common_src = f"{prefix}/src/**/*.[csS]"
|
||||
common_src = glob.glob(common_src, recursive=True)
|
||||
log.debug(f"> common src = {common_src}")
|
||||
|
||||
board_src = f"{prefix}/boards/{build.target}/src/**/*.[csS]"
|
||||
board_src = glob.glob(board_src, recursive=True)
|
||||
log.debug(f"> board src = {board_src}")
|
||||
|
||||
prefix = f"{build.prefix['build']}/bootloader/"
|
||||
include_list = [
|
||||
f"{build.prefix['bootloader']}/include/",
|
||||
f"{build.prefix['bootloader']}/boards/{build.target}/include/"
|
||||
]
|
||||
return cmake_build_configure(
|
||||
prefix,
|
||||
{
|
||||
'name' : 'bootloader',
|
||||
'version' : version,
|
||||
'linker' : f"{build.prefix['bootloader']}/bootloader.ld",
|
||||
'ldflags' : compiles.ldflags,
|
||||
'cflags' : compiles.cflags,
|
||||
'include' : include_list,
|
||||
'src' : assets_src + common_src + board_src,
|
||||
'libs' : compiles.libs,
|
||||
'toolchain_prefix' : compiles.toolchain_prefix,
|
||||
'toolchain_proc' : compiles.toolchain_processor,
|
||||
}
|
||||
)
|
|
@ -0,0 +1,186 @@
|
|||
"""
|
||||
CMake abstraction
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'cmake_build_configure',
|
||||
'cmake_build_compile',
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
_CMAKE_TOOLCHAIN_TEMPLATE = """
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
set(CMAKE_SYSTEM_PROCESSOR {VXDEV_TOOLCHAIN_PROCESSOR})
|
||||
|
||||
set(CMAKE_C_COMPILER {VXDEV_TOOLCHAIN_PREFIX}gcc)
|
||||
set(CMAKE_CXX_COMPILER {VXDEV_TOOLCHAIN_PREFIX}g++)
|
||||
|
||||
set(CMAKE_C_FLAGS_INIT "")
|
||||
set(CMAKE_CXX_FLAGS_INIT "")
|
||||
|
||||
# required to avoid CMake compiler check fails
|
||||
add_compile_options(-nostdlib)
|
||||
add_link_options(-nostdlib)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
""".strip()
|
||||
|
||||
_CMAKE_TEMPLATE = """
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
#---
|
||||
# project-specific information
|
||||
#---
|
||||
|
||||
project({VXDEV_PROJ_NAME} VERSION {VXDEV_PROJ_VERSION} LANGUAGES C ASM)
|
||||
|
||||
include_directories(
|
||||
{VXDEV_BUILD_INCLUDES}
|
||||
)
|
||||
add_compile_options(
|
||||
{VXDEV_BUILD_CFLAGS}
|
||||
)
|
||||
add_link_options(
|
||||
{VXDEV_BUILD_LDFLAGS}
|
||||
)
|
||||
set(
|
||||
CMAKE_EXE_LINKER_FLAGS
|
||||
"${CMAKE_EXE_LINKER_FLAGS} -T {VXDEV_BUILD_LINKER}"
|
||||
)
|
||||
|
||||
#---
|
||||
# source files listing
|
||||
#---
|
||||
|
||||
set(
|
||||
VXDEV_PROJ_SOURCES
|
||||
{VXDEV_SOURCE_FILES}
|
||||
)
|
||||
|
||||
#---
|
||||
# commit projet
|
||||
#---
|
||||
|
||||
add_executable({VXDEV_PROJ_NAME} ${VXDEV_PROJ_SOURCES})
|
||||
target_link_libraries({VXDEV_PROJ_NAME} {VXDEV_BUILD_LIBS})
|
||||
""".strip()
|
||||
|
||||
def _cmakefile_need_update(cmakefile_pathname, content):
|
||||
""" check if the current CMakeLists.txt need to be recreated
|
||||
"""
|
||||
if not os.path.exists(cmakefile_pathname):
|
||||
return True
|
||||
with open(cmakefile_pathname, 'r', encoding='ascii') as cmakefile:
|
||||
if cmakefile.read() != content:
|
||||
os.remove(cmakefile_pathname)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _cmake_build_generate_template(prefix, proj):
|
||||
""" Generate a common CMake file
|
||||
|
||||
@args
|
||||
> proj (dict) - project information
|
||||
> prefix (str) - build prefix
|
||||
"""
|
||||
cmakefile_prefix = f"{prefix}"
|
||||
cmakefile_pathname = f"{cmakefile_prefix}/CMakeLists.txt"
|
||||
|
||||
proj['src'] = '\n '.join(proj['src'])
|
||||
proj['include'] = '\n '.join(proj['include'])
|
||||
proj['ldflags'] = '\n '.join(proj['ldflags'])
|
||||
proj['cflags'] = '\n '.join(proj['cflags'])
|
||||
proj['libs'] = '\n '.join(proj['libs'])
|
||||
|
||||
content = _CMAKE_TEMPLATE
|
||||
content = re.sub('{VXDEV_PROJ_NAME}', proj['name'], content)
|
||||
content = re.sub('{VXDEV_PROJ_VERSION}', proj['version'], content)
|
||||
content = re.sub('{VXDEV_BUILD_CFLAGS}', proj['cflags'], content)
|
||||
content = re.sub('{VXDEV_BUILD_LDFLAGS}', proj['ldflags'], content)
|
||||
content = re.sub('{VXDEV_BUILD_LINKER}', proj['linker'], content)
|
||||
content = re.sub('{VXDEV_SOURCE_FILES}', proj['src'], content)
|
||||
content = re.sub('{VXDEV_BUILD_INCLUDES}', proj['include'], content)
|
||||
content = re.sub('{VXDEV_BUILD_LIBS}', proj['libs'], content)
|
||||
|
||||
if not _cmakefile_need_update(cmakefile_pathname, content):
|
||||
return False
|
||||
if not os.path.exists(cmakefile_prefix):
|
||||
os.makedirs(cmakefile_prefix)
|
||||
|
||||
log.debug(f"generate cmakefile at '{cmakefile_prefix}'...")
|
||||
with open(cmakefile_pathname, 'x', encoding='ascii') as cmakefile:
|
||||
cmakefile.write(content)
|
||||
return True
|
||||
|
||||
def _cmake_build_generate_toolchain(prefix, proj):
|
||||
""" generate the toolchain file
|
||||
"""
|
||||
toolchain_proc = proj['toolchain_proc']
|
||||
toolchain_prefix = proj['toolchain_prefix']
|
||||
cmakefile_pathname = f"{prefix}/toolchain.cmake"
|
||||
|
||||
content = _CMAKE_TOOLCHAIN_TEMPLATE
|
||||
content = re.sub('{VXDEV_TOOLCHAIN_PROCESSOR}', toolchain_proc, content)
|
||||
content = re.sub('{VXDEV_TOOLCHAIN_PREFIX}', toolchain_prefix, content)
|
||||
|
||||
if not _cmakefile_need_update(cmakefile_pathname, content):
|
||||
return False
|
||||
if not os.path.exists(prefix):
|
||||
os.makedirs(prefix)
|
||||
|
||||
log.debug(f"generate cmakefile at '{prefix}'...")
|
||||
with open(cmakefile_pathname, 'x', encoding='ascii') as cmakefile:
|
||||
cmakefile.write(content)
|
||||
return True
|
||||
|
||||
#---
|
||||
# Pulbic
|
||||
#---
|
||||
|
||||
def cmake_build_configure(prefix, proj):
|
||||
""" Abstract cmake configuration
|
||||
|
||||
@args
|
||||
> name (str) - project name
|
||||
> prefix (str) - build prefix
|
||||
"""
|
||||
toolchain_flag = ''
|
||||
check = _cmake_build_generate_toolchain(prefix, proj)
|
||||
if proj['toolchain_prefix']:
|
||||
check += _cmake_build_generate_template(prefix, proj)
|
||||
toolchain_flag = f"-DCMAKE_TOOLCHAIN_FILE={prefix}/toolchain.cmake"
|
||||
if check == 0:
|
||||
return
|
||||
shell_cmd = f"cmake {toolchain_flag} -B {prefix} -S {prefix}"
|
||||
if subprocess.run(shell_cmd.split(), check=False).returncode != 0:
|
||||
log.emergency(f"{proj['name']}: unable to configure the projet, abord")
|
||||
|
||||
def cmake_build_compile(name, prefix, verbose):
|
||||
""" Abstract cmake configuration
|
||||
|
||||
@args
|
||||
> name (str) - project name
|
||||
> prefix (str) - build prefix
|
||||
> verbose (bool) - build verbose
|
||||
|
||||
@return
|
||||
> the generated binary pathname
|
||||
"""
|
||||
shell_cmd = f"cmake --build {prefix}"
|
||||
if verbose:
|
||||
shell_cmd += ' --verbose'
|
||||
if subprocess.run(shell_cmd.split(), check=False).returncode != 0:
|
||||
log.emergency(f"{name}: unable to configure the projet, abord")
|
||||
return f"{prefix}/{name}"
|
|
@ -0,0 +1,56 @@
|
|||
"""
|
||||
core.build.compiles - handle "compiles.toml" files
|
||||
"""
|
||||
import dataclasses
|
||||
import toml
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'compiles_fetch',
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CompilesInfo():
|
||||
""" board compilation information """
|
||||
toolchain_prefix = ''
|
||||
toolchain_processor = ''
|
||||
toolchain_endian = ''
|
||||
cflags = []
|
||||
ldflags = []
|
||||
libs = []
|
||||
assets = []
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def compiles_fetch(filename):
|
||||
""" try to load the compiles.toml file
|
||||
"""
|
||||
board_info = toml.load(filename)
|
||||
info = CompilesInfo()
|
||||
|
||||
check = 'VXDEV_TOOLCHAIN_PREFIX' in board_info
|
||||
check += 'VXDEV_TOOLCHAIN_PROCESSOR' in board_info
|
||||
check += 'VXDEV_TOOLCHAIN_ENDIANNESS' in board_info
|
||||
if check != 3:
|
||||
log.emergency(f"{filename}: missing toolchain information, abord")
|
||||
|
||||
info.toolchain_prefix = board_info['VXDEV_TOOLCHAIN_PREFIX']
|
||||
info.toolchain_processor = board_info['VXDEV_TOOLCHAIN_PROCESSOR']
|
||||
info.toolchain_endian = board_info['VXDEV_TOOLCHAIN_ENDIANNESS']
|
||||
if 'VXDEV_CFLAGS' in board_info:
|
||||
info.cflags = board_info['VXDEV_CFLAGS']
|
||||
if 'VXDEV_LDFLAGS' in board_info:
|
||||
info.ldflags = board_info['VXDEV_LDFLAGS']
|
||||
if 'VXDEV_LIBS' in board_info:
|
||||
info.libs = board_info['VXDEV_LIBS']
|
||||
if 'VXDEV_ASSETS' in board_info:
|
||||
info.assets = board_info['VXDEV_ASSETS']
|
||||
|
||||
return info
|
|
@ -0,0 +1,139 @@
|
|||
"""
|
||||
vxdev.core.build.kernel - kernel build abstraction
|
||||
"""
|
||||
import os
|
||||
import glob
|
||||
import toml
|
||||
|
||||
from core.logger import log
|
||||
from core.build.cmake import cmake_build_configure
|
||||
from core.build.compiles import compiles_fetch
|
||||
|
||||
__all__ = [
|
||||
'kernel_configure',
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _fetch_board_config(build, board_cfg_path):
|
||||
""" try to find and load the board configuratio file
|
||||
|
||||
This routine will check the TOML boards configuration validity and return
|
||||
only the 'config' section which contain most of the information needed to
|
||||
fetch all kernel source files
|
||||
|
||||
@return
|
||||
> a dictionary with all configuration information
|
||||
> give up if an error is detected
|
||||
"""
|
||||
if not os.path.exists(board_cfg_path):
|
||||
log.emergency("unable to find the kernel '{build.target}' board file")
|
||||
log.debug(f"> board config = {board_cfg_path}")
|
||||
board_cfg = toml.load(board_cfg_path)
|
||||
if 'meta' not in board_cfg:
|
||||
log.emergency(f"{build.target}: missing board 'meta' information")
|
||||
if 'config' not in board_cfg:
|
||||
log.emergency(f"{build.target}: missing board 'config' information")
|
||||
if 'modules' not in board_cfg['config']:
|
||||
log.emergency(f"{build.target}: missing board 'modules' information")
|
||||
if 'drivers' not in board_cfg['config']:
|
||||
log.emergency(f"{build.target}: missing board 'drivers' information")
|
||||
return board_cfg['config']
|
||||
|
||||
def _fetch_board_sources(build, prefix):
|
||||
""" fetch board specific sources files
|
||||
|
||||
Scan the target board source directory (<prefix>/src/) and fetch all C
|
||||
and assembly file.
|
||||
|
||||
@return
|
||||
> a list of all source file found
|
||||
"""
|
||||
prefix = f"{prefix}/boards/{build.target}/src"
|
||||
if not os.path.exists(prefix):
|
||||
return glob.glob(f"{prefix}/**/*.[csS]", recursive=True)
|
||||
return []
|
||||
|
||||
def _fetch_common_sources(_, prefix):
|
||||
""" fetch common source file
|
||||
"""
|
||||
return glob.glob(f"{prefix}/src/*.[csS]", recursive=False)
|
||||
|
||||
def _fetch_drivers_sources(build, prefix, drvlist):
|
||||
""" fetch selected drivers
|
||||
"""
|
||||
sources = []
|
||||
for drv in drvlist:
|
||||
if not os.path.exists(f"{prefix}/src/drivers/{drv}"):
|
||||
log.emergency(f"{build.target}: unable to find driver '{drv}'")
|
||||
sources += glob.glob(
|
||||
f"{prefix}/src/drivers/{drv}/**/*.[csS]",
|
||||
recursive=True
|
||||
)
|
||||
return sources
|
||||
|
||||
def _fetch_modules_sources(build, prefix, modlist):
|
||||
""" fetch selected kernel module
|
||||
"""
|
||||
sources = []
|
||||
for mod in modlist:
|
||||
if not os.path.exists(f"{prefix}/src/modules/{mod}"):
|
||||
log.emergency(f"{build.target}: unable to find module '{mod}'")
|
||||
sources += glob.glob(
|
||||
f"{prefix}/src/modules/{mod}/**/*.[csS]",
|
||||
recursive=True
|
||||
)
|
||||
return sources
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def kernel_configure(build, version):
|
||||
""" configure the kernel
|
||||
|
||||
TODO
|
||||
"""
|
||||
prefix = build.prefix['kernel']
|
||||
log.debug('fetching kernel files...')
|
||||
log.debug(f"> prefix = {prefix}")
|
||||
|
||||
log.debug('- fetching compiles informations...')
|
||||
compiles = compiles_fetch(f"{prefix}/boards/{build.target}/compiles.toml")
|
||||
if not compiles:
|
||||
log.emergency(
|
||||
"unable to find kernel's compilation information (compiles.toml)"
|
||||
)
|
||||
|
||||
board_cfg = _fetch_board_config(
|
||||
build,
|
||||
f"{prefix}/boards/{build.target}/board.toml"
|
||||
)
|
||||
sources = _fetch_board_sources(build, prefix)
|
||||
sources += _fetch_common_sources(build, prefix)
|
||||
sources += _fetch_drivers_sources(build, prefix, board_cfg['drivers'])
|
||||
sources += _fetch_modules_sources(build, prefix, board_cfg['modules'])
|
||||
log.debug("> fetched sources = {sources}")
|
||||
|
||||
prefix = f"{build.prefix['build']}/kernel/"
|
||||
include_list = [
|
||||
f"{build.prefix['kernel']}/include/",
|
||||
f"{build.prefix['kernel']}/boards/{build.target}/include/"
|
||||
]
|
||||
return cmake_build_configure(
|
||||
prefix,
|
||||
{
|
||||
'name' : 'kernel',
|
||||
'version' : version,
|
||||
'linker' : f"{build.prefix['kernel']}/kernel.ld",
|
||||
'ldflags' : compiles.ldflags,
|
||||
'cflags' : compiles.cflags,
|
||||
'include' : include_list,
|
||||
'src' : sources,
|
||||
'libs' : compiles.libs,
|
||||
'toolchain_prefix' : compiles.toolchain_prefix,
|
||||
'toolchain_proc' : compiles.toolchain_processor,
|
||||
}
|
||||
)
|
|
@ -1,65 +0,0 @@
|
|||
"""
|
||||
CMake abstraction
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
__all__ = [
|
||||
'cmake_configure',
|
||||
'cmake_build',
|
||||
'cmake_install',
|
||||
'cmake_uninstall',
|
||||
]
|
||||
|
||||
#---
|
||||
# Pulbic
|
||||
#---
|
||||
|
||||
def cmake_configure(prefix_build, prefix_src):
|
||||
""" Abstract cmake configuration """
|
||||
toolchain_flag = ''
|
||||
if toolchain_path := os.environ.get('VXSDK_HOOK_CMAKE_TOOLCHAIN'):
|
||||
toolchain_flag = f"-DCMAKE_TOOLCHAIN_FILE={toolchain_path}"
|
||||
shell_cmd = f"cmake {toolchain_flag} -B {prefix_build} -S {prefix_src}"
|
||||
return subprocess.run(shell_cmd.split(), check=False).returncode
|
||||
|
||||
def cmake_build(prefix_build, verbose):
|
||||
""" Abstract cmake configuration """
|
||||
shell_cmd = f"cmake --build {prefix_build}"
|
||||
if verbose:
|
||||
shell_cmd += ' --verbose'
|
||||
return subprocess.run(shell_cmd.split(), check=False).returncode
|
||||
|
||||
def cmake_install(prefix_build, verbose):
|
||||
""" Abstract cmake installation """
|
||||
shell_cmd = f"cmake --install {prefix_build}"
|
||||
if verbose:
|
||||
shell_cmd += ' --verbose'
|
||||
return subprocess.run(shell_cmd.split(), check=False).returncode
|
||||
|
||||
def cmake_uninstall(prefix_build, verbose):
|
||||
""" Abstract cmake uninstall
|
||||
|
||||
Note that CMake does not offert a easy way to uninstall project, but it
|
||||
generate a file which contains all installed pathname
|
||||
"""
|
||||
manifile = f"{prefix_build}/install_manifest.txt"
|
||||
if not os.path.exists(manifile):
|
||||
print('project not installed')
|
||||
return -1
|
||||
|
||||
retcode = 0
|
||||
with open(manifile, 'r', encoding='utf8') as manifest:
|
||||
for pathname in manifest.readlines():
|
||||
pathname = pathname.strip()
|
||||
if not os.path.exists(pathname):
|
||||
continue
|
||||
if verbose:
|
||||
print("-- Removing {pathname}")
|
||||
ret = subprocess.run(f"rm {pathname}".split(), check=False)
|
||||
if ret.returncode == 0:
|
||||
continue
|
||||
print("warning : error during removing file", file=sys.stderr)
|
||||
retcode -= 1
|
||||
return retcode
|
|
@ -1,38 +0,0 @@
|
|||
"""
|
||||
Configuration file handling
|
||||
"""
|
||||
import os
|
||||
|
||||
__all__ = [
|
||||
'config_file_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def config_file_generate(kernconf, confile_prefix):
|
||||
""" Generate the kernel configuration file for CMake build system
|
||||
|
||||
@args
|
||||
> kernconf (dict) - kernel configuration information
|
||||
> confile_prefix (str) - build prefix
|
||||
|
||||
@return
|
||||
> 0 on success, negative value otherwise
|
||||
"""
|
||||
if not os.path.exists(confile_prefix):
|
||||
os.makedirs(confile_prefix)
|
||||
|
||||
content = "# file generated by the vxSDK\n\n"
|
||||
for item in kernconf.items():
|
||||
if not (data := item[1]):
|
||||
continue
|
||||
if isinstance(item[1], list):
|
||||
data = ' '.join(item[1])
|
||||
content += f"set({item[0]} {data})\n"
|
||||
|
||||
confile_path = f"{confile_prefix}/vxkernel_config.cmake"
|
||||
with open(confile_path, 'w+', encoding='utf8') as file:
|
||||
file.write(content)
|
||||
return 0
|
|
@ -1,34 +0,0 @@
|
|||
"""
|
||||
Format handling
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'format_select',
|
||||
'format_command',
|
||||
]
|
||||
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def format_select(_, __):
|
||||
""" Select a kernel format and generate output information
|
||||
|
||||
@args
|
||||
> kernconf (dict) - kernel configuration information
|
||||
> format (str) - targeted format
|
||||
|
||||
@return
|
||||
> Nothing, hangout if error. (return are only here for syntax sugar)
|
||||
"""
|
||||
|
||||
def format_command(_):
|
||||
""" format-specific handler
|
||||
|
||||
@arg
|
||||
> argv (list,str) - subcommand arguments
|
||||
|
||||
@return
|
||||
> Never
|
||||
"""
|
|
@ -0,0 +1,127 @@
|
|||
"""
|
||||
core.log - logger object
|
||||
"""
|
||||
import sys
|
||||
|
||||
__all__ = [
|
||||
'log'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
_LOG_DEBUG = 7
|
||||
_LOG_INFO = 6
|
||||
_LOG_NOTICE = 5
|
||||
_LOG_USER = 4
|
||||
_LOG_WARN = 3
|
||||
_LOG_ERR = 2
|
||||
_LOG_CRIT = 1
|
||||
_LOG_EMERG = 0
|
||||
|
||||
|
||||
class _VxLogger():
|
||||
def __init__(self, logfile=None):
|
||||
self._logfile = logfile
|
||||
self._level = _LOG_USER
|
||||
self._indent = 0
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _print(self, level, text, skip_indent, fileno):
|
||||
if self._level < level:
|
||||
return 0
|
||||
if not skip_indent and self._level == _LOG_DEBUG and self._indent > 0:
|
||||
text = ('>>> ' * self._indent) + text
|
||||
print(text, file=fileno, end='', flush=True)
|
||||
return len(text) + 1
|
||||
|
||||
#---
|
||||
# Public properties
|
||||
#---
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
""" <property> handle print level """
|
||||
return self._level
|
||||
|
||||
@level.setter
|
||||
def level(self, level):
|
||||
""" <property> handle print level """
|
||||
if level < _LOG_EMERG or level > _LOG_DEBUG:
|
||||
print(f"[log] level update to {level} is not possible, ignored")
|
||||
return
|
||||
self._level = level
|
||||
|
||||
@property
|
||||
def indent(self):
|
||||
""" <property> handle indentation level for _LOG_DEBUG """
|
||||
return self._indent
|
||||
|
||||
@indent.setter
|
||||
def indent(self, indent):
|
||||
""" <property> handle indentation level for _LOG_DEBUG """
|
||||
if indent < 0:
|
||||
print(f"[log] indent update to {indent} is not possible, ignored")
|
||||
return
|
||||
self._indent = indent
|
||||
|
||||
#---
|
||||
# Public methods
|
||||
#---
|
||||
|
||||
def debug(self, text, end='\n', skip_indent=False):
|
||||
""" print debug log """
|
||||
return self._print(
|
||||
_LOG_DEBUG, f"[DEBUG] {text}{end}", skip_indent, sys.stdout
|
||||
)
|
||||
|
||||
def info(self, text, end='\n', skip_indent=False):
|
||||
""" print info log """
|
||||
return self._print(
|
||||
_LOG_INFO, f"[INFO] {text}{end}", skip_indent, sys.stdout
|
||||
)
|
||||
|
||||
def notice(self, text, end='\n', skip_indent=False):
|
||||
""" print notice log """
|
||||
return self._print(
|
||||
_LOG_NOTICE, f"[NOTICE] {text}{end}", skip_indent, sys.stdout
|
||||
)
|
||||
|
||||
def user(self, text, end='\n', skip_indent=False):
|
||||
""" print user log """
|
||||
return self._print(_LOG_USER, f"{text}{end}", skip_indent, sys.stdout)
|
||||
|
||||
def warn(self, text, end='\n', skip_indent=False):
|
||||
""" print warning log """
|
||||
return self._print(
|
||||
_LOG_WARN, f"[WARN] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
|
||||
def error(self, text, end='\n', skip_indent=False):
|
||||
""" print error log """
|
||||
return self._print(
|
||||
_LOG_ERR, f"[ERROR] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
|
||||
def critical(self, text, end='\n', skip_indent=False):
|
||||
""" print critical log """
|
||||
return self._print(
|
||||
_LOG_CRIT, f"[CRITICAL] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
|
||||
def emergency(self, text, end='\n', skip_indent=False):
|
||||
""" print emergency log """
|
||||
self._print(
|
||||
_LOG_EMERG, f"[EMERGENCY] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
sys.exit(84)
|
||||
|
||||
#---
|
||||
# Public functions
|
||||
#---
|
||||
|
||||
log = _VxLogger()
|
|
@ -0,0 +1,78 @@
|
|||
"""
|
||||
core.unitests - unit tests abstraction
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from core.logger import log
|
||||
from core.unitests.bootloader import bootloader_unitest_config
|
||||
from core.unitests.cmake import cmake_unitest_compile
|
||||
from core.unitests.criterion import criterion_unitests_run
|
||||
from core.unitests.gcovr import gcovr_unitests_run
|
||||
|
||||
__all__ = [
|
||||
'VxOSUnitest'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
class VxOSUnitest():
|
||||
""" Unit tests abstraction
|
||||
"""
|
||||
def __init__(self, verbose):
|
||||
self._verbose = verbose
|
||||
prefix_base = f"{os.path.dirname(__file__)}/../../../../"
|
||||
prefix_base = os.path.normpath(prefix_base)
|
||||
self._prefix = {
|
||||
'base' : prefix_base,
|
||||
'build' : f"{prefix_base}/.vxsdk/unitests",
|
||||
'src' : f"{prefix_base}/vxgos",
|
||||
}
|
||||
self._unitpath = {
|
||||
'bootloader' : [],
|
||||
'kernel' : [],
|
||||
'os' : [],
|
||||
}
|
||||
|
||||
#---
|
||||
# Properties
|
||||
#---
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
""" return the prefix dictionary """
|
||||
return self._prefix
|
||||
|
||||
#---
|
||||
# Methods
|
||||
#---
|
||||
|
||||
def configure(self):
|
||||
""" generate all unitests env """
|
||||
self._unitpath['bootloader'] = bootloader_unitest_config(self)
|
||||
|
||||
def execute(self):
|
||||
""" execute all unitests
|
||||
"""
|
||||
bininfo_list = []
|
||||
log.user('- compile unitests...')
|
||||
for item in self._unitpath.items():
|
||||
for unitest in item[1]:
|
||||
log.user(f" > {item[0]}:{unitest['name']}...")
|
||||
bininfo_list.append((
|
||||
f"{item[0]}:{unitest['name']}",
|
||||
cmake_unitest_compile(
|
||||
unitest['name'],
|
||||
unitest['prefix'],
|
||||
self._verbose
|
||||
),
|
||||
unitest['requirements']
|
||||
))
|
||||
log.user('- execute unitests...')
|
||||
for bininfo in bininfo_list:
|
||||
log.user(f" > {bininfo[0]}...")
|
||||
if criterion_unitests_run(bininfo) != 0:
|
||||
continue
|
||||
gcovr_unitests_run(bininfo)
|
|
@ -0,0 +1,60 @@
|
|||
"""
|
||||
core.unitests.bootloader - bootloader unit tests abstraction
|
||||
"""
|
||||
import os
|
||||
import glob
|
||||
|
||||
from core.logger import log
|
||||
from core.unitests.requirements import requirements_unitests_parse
|
||||
from core.unitests.cmake import cmake_unitest_configure
|
||||
|
||||
__all__ = [
|
||||
'bootloader_unitest_config'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _bootloader_unitest_new(unitest, prefix, test):
|
||||
""" add a new bootloader unitest
|
||||
"""
|
||||
log.debug(f"- new unitest '{test}'...")
|
||||
|
||||
req = requirements_unitests_parse(prefix, 'bootloader', test)
|
||||
log.debug(f"> deplist = {req}")
|
||||
|
||||
srclist = f"{prefix}/unitests/{test}/**/*.[csS]"
|
||||
srclist = glob.glob(srclist, recursive=True)
|
||||
log.debug(f"> srclist = {srclist}")
|
||||
|
||||
include_list = [
|
||||
f"{prefix}/include/",
|
||||
f"{prefix}/unitests/{test}/"
|
||||
]
|
||||
|
||||
return {
|
||||
'name' : test,
|
||||
'prefix' : cmake_unitest_configure(
|
||||
f"{unitest.prefix['build']}/bootloader/{test}",
|
||||
{
|
||||
'name' : test,
|
||||
'src' : req['covered'] + req['dependencies'] + srclist,
|
||||
'include' : req['includes'] + include_list
|
||||
}
|
||||
),
|
||||
'requirements' : req
|
||||
}
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def bootloader_unitest_config(unitest):
|
||||
""" bootloader unitests abstraction entry
|
||||
"""
|
||||
unitpath = []
|
||||
prefix = f"{unitest.prefix['src']}/bootloader"
|
||||
for test in os.listdir(f"{prefix}/unitests/"):
|
||||
unitpath.append(_bootloader_unitest_new(unitest, prefix, test))
|
||||
return unitpath
|
|
@ -0,0 +1,135 @@
|
|||
"""
|
||||
core.unitests.cmake - CMake abstraction for unitests
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'cmake_unitest_configure',
|
||||
'cmake_unitest_compile',
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
_CMAKE_TEMPLATE = """
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
#---
|
||||
# project-specific information
|
||||
#---
|
||||
|
||||
project({VXDEV_PROJ_NAME} VERSION 0.1.0 LANGUAGES C ASM)
|
||||
|
||||
include_directories(
|
||||
{VXDEV_BUILD_INCLUDES}
|
||||
)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-std=c2x
|
||||
-g3
|
||||
--coverage
|
||||
-fsanitize=address
|
||||
)
|
||||
add_link_options(
|
||||
--coverage
|
||||
-lasan
|
||||
-lcriterion
|
||||
)
|
||||
|
||||
#---
|
||||
# source files listing
|
||||
#---
|
||||
|
||||
set(
|
||||
VXDEV_PROJ_SOURCES
|
||||
{VXDEV_SOURCE_FILES}
|
||||
)
|
||||
|
||||
#---
|
||||
# commit projet
|
||||
#---
|
||||
|
||||
add_executable({VXDEV_PROJ_NAME} ${VXDEV_PROJ_SOURCES})
|
||||
target_link_libraries({VXDEV_PROJ_NAME})
|
||||
""".strip()
|
||||
|
||||
def _cmakefile_need_update(cmakefile_pathname, content):
|
||||
""" check if the current CMakeLists.txt need to be recreated
|
||||
"""
|
||||
if not os.path.exists(cmakefile_pathname):
|
||||
return True
|
||||
with open(cmakefile_pathname, 'r', encoding='ascii') as cmakefile:
|
||||
if cmakefile.read() != content:
|
||||
os.remove(cmakefile_pathname)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _cmake_unitest_generate_template(prefix, proj):
|
||||
""" Generate a common CMake file
|
||||
|
||||
@args
|
||||
> proj (dict) - project information
|
||||
> prefix (str) - build prefix
|
||||
"""
|
||||
cmakefile_prefix = f"{prefix}"
|
||||
cmakefile_pathname = f"{cmakefile_prefix}/CMakeLists.txt"
|
||||
|
||||
proj['src'] = '\n '.join(proj['src'])
|
||||
proj['include'] = '\n '.join(proj['include'])
|
||||
|
||||
content = _CMAKE_TEMPLATE
|
||||
content = re.sub('{VXDEV_PROJ_NAME}', proj['name'], content)
|
||||
content = re.sub('{VXDEV_SOURCE_FILES}', proj['src'], content)
|
||||
content = re.sub('{VXDEV_BUILD_INCLUDES}', proj['include'], content)
|
||||
|
||||
if not _cmakefile_need_update(cmakefile_pathname, content):
|
||||
return False
|
||||
if not os.path.exists(cmakefile_prefix):
|
||||
os.makedirs(cmakefile_prefix)
|
||||
|
||||
log.debug(f"generate cmakefile at '{cmakefile_prefix}'...")
|
||||
with open(cmakefile_pathname, 'x', encoding='ascii') as cmakefile:
|
||||
cmakefile.write(content)
|
||||
return True
|
||||
|
||||
#---
|
||||
# Pulbic
|
||||
#---
|
||||
|
||||
def cmake_unitest_configure(prefix, proj):
|
||||
""" Abstract cmake configuration
|
||||
|
||||
@args
|
||||
> name (str) - project name
|
||||
> prefix (str) - build prefix
|
||||
"""
|
||||
if not _cmake_unitest_generate_template(prefix, proj):
|
||||
return prefix
|
||||
shell_cmd = f"cmake -B {prefix} -S {prefix}"
|
||||
if subprocess.run(shell_cmd.split(), check=False).returncode != 0:
|
||||
log.emergency(f"{proj['name']}: unable to configure the projet, abord")
|
||||
return prefix
|
||||
|
||||
def cmake_unitest_compile(name, prefix, verbose):
|
||||
""" Abstract cmake configuration
|
||||
|
||||
@args
|
||||
> name (str) - project name
|
||||
> prefix (str) - build prefix
|
||||
> verbose (bool) - build verbose
|
||||
|
||||
@return
|
||||
> the generated binary pathname
|
||||
"""
|
||||
shell_cmd = f"cmake --build {prefix}"
|
||||
if verbose:
|
||||
shell_cmd += ' --verbose'
|
||||
if subprocess.run(shell_cmd.split(), check=False).returncode != 0:
|
||||
log.emergency(f"{name}: unable to configure the projet, abord")
|
||||
return f"{prefix}/{name}"
|
|
@ -0,0 +1,29 @@
|
|||
"""
|
||||
unitests.criterion - criterion abstraction
|
||||
"""
|
||||
import os
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'criterion_unitests_run'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def criterion_unitests_run(bininfo):
|
||||
""" run criterion unitests (purge converage informaiton)
|
||||
"""
|
||||
log.debug(f"{bininfo[0]}: purge GCDA file...")
|
||||
query = f"{os.path.dirname(bininfo[1])}/**/*.gcd[ao]"
|
||||
for gcda in glob.glob(query, recursive=True):
|
||||
log.debug(f"- {gcda}...")
|
||||
os.remove(gcda)
|
||||
if subprocess.run([bininfo[1]], check=False).returncode != 0:
|
||||
log.error(f"{bininfo[0]}: unitest error, skipped code coverage")
|
||||
return -2
|
||||
return 0
|
|
@ -0,0 +1,73 @@
|
|||
"""
|
||||
core.unitests.gcovr - gcovr abstraction
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from core.logger import log
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _gcovr_parse_output(output, covered_list):
|
||||
""" fetch only line information
|
||||
"""
|
||||
lineinfo = []
|
||||
workaround_line = None
|
||||
for line in output.decode('ascii').split('\n'):
|
||||
if not (line := line.split()):
|
||||
continue
|
||||
if workaround_line:
|
||||
lineinfo.append(workaround_line + line)
|
||||
workaround_line = None
|
||||
continue
|
||||
find = False
|
||||
for filename in covered_list:
|
||||
if filename.find(line[0]) >= 0:
|
||||
find = True
|
||||
break
|
||||
if not find:
|
||||
continue
|
||||
if len(line) < 2:
|
||||
workaround_line = line
|
||||
continue
|
||||
lineinfo.append(line)
|
||||
return lineinfo
|
||||
|
||||
def _gcovr_display_output(lineinfo):
|
||||
""" proper display line information
|
||||
"""
|
||||
maxwidth = 0
|
||||
for line in lineinfo:
|
||||
maxwidth = max(len(line[0]), maxwidth)
|
||||
for line in lineinfo:
|
||||
percent = int(line[3][:-1])
|
||||
color = '\033[31m'
|
||||
if 50 <= percent <= 90:
|
||||
color = '\033[33m'
|
||||
if percent >= 90:
|
||||
color = '\033[32m'
|
||||
if percent == 100:
|
||||
log.user(f"[\033[92m{line[3]: >4}\033[0m] {line[0]: <{maxwidth}}")
|
||||
continue
|
||||
log.user(
|
||||
f"[{color}{line[3]: >4}\033[0m] {line[0]: <{maxwidth}} {line[4]}"
|
||||
)
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def gcovr_unitests_run(bininfo):
|
||||
""" run code coverage
|
||||
"""
|
||||
pwd = os.path.dirname(bininfo[1])
|
||||
cmd = f"gcovr {pwd}"
|
||||
proc = subprocess.run(cmd.split(), capture_output=True, check=False)
|
||||
if proc.returncode != 0:
|
||||
log.error(f"{bininfo[0]}: code coverage error, skipped")
|
||||
log.error(proc.stderr.decode('utf8'))
|
||||
return -1
|
||||
lineinfo = _gcovr_parse_output(proc.stdout, bininfo[2]['covered'])
|
||||
_gcovr_display_output(lineinfo)
|
||||
return 0
|
|
@ -0,0 +1,68 @@
|
|||
"""
|
||||
core.unitests.requirements - requirements.txt file parser
|
||||
"""
|
||||
import os
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'requirements_unitests_parse'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def requirements_unitests_parse(prefix, suite, test):
|
||||
""" parse requirements.txt file for unitests
|
||||
|
||||
@args
|
||||
> prefix (str) - unitests root dir
|
||||
> suite (str) - unitests suite (bootloader, kernel, ...)
|
||||
> test (str) - unitests name
|
||||
|
||||
@return
|
||||
> a dictionary with parsed information
|
||||
"""
|
||||
testname = f"{suite}:{test}"
|
||||
pathname = f"{prefix}/unitests/{test}/requirements.txt"
|
||||
if not os.path.exists(prefix):
|
||||
log.emergency(f"{pathname}: file doesn't exists, skipped")
|
||||
|
||||
line_id = 0
|
||||
requirement = {
|
||||
'covered' : [],
|
||||
'dependencies' : [],
|
||||
'includes' : []
|
||||
}
|
||||
mapping = {
|
||||
'c' : requirement['covered'],
|
||||
'd' : requirement['dependencies'],
|
||||
'i' : requirement['includes'],
|
||||
}
|
||||
with open(pathname, 'r', encoding='ascii') as reqfile:
|
||||
while True:
|
||||
line_id += 1
|
||||
if not (line := reqfile.readline()):
|
||||
break
|
||||
if not (line := line.strip()):
|
||||
continue
|
||||
if line.strip()[0] == '#':
|
||||
continue
|
||||
line = line.split()
|
||||
if line[0][0] != '[' or line[0][2] != ']':
|
||||
log.warn(f"{testname}:requirements.txt:{line_id}: line error")
|
||||
continue
|
||||
if line[0][1] not in mapping:
|
||||
log.warn(f"{testname}: unsupported '{line[0][1]}' flag")
|
||||
continue
|
||||
real_filename = f"{prefix}/{line[1]}"
|
||||
if not os.path.exists(real_filename):
|
||||
log.emergency(
|
||||
f"{testname}: dependency '{real_filename}'"
|
||||
"not exists"
|
||||
)
|
||||
mapping[line[0][1]].append(real_filename)
|
||||
|
||||
log.debug(f"- requirements.txt : {requirement}")
|
||||
return requirement
|
|
@ -0,0 +1,17 @@
|
|||
"""
|
||||
version - centralize all version information
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'VXGOS_OS_VERSION',
|
||||
'VXGOS_KERNEL_VERSION',
|
||||
'VXGOS_BOOTLOADER_VERSION',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
VXGOS_OS_VERSION = '0.0.0'
|
||||
VXGOS_KERNEL_VERSION = '0.7.0'
|
||||
VXGOS_BOOTLOADER_VERSION = '0.0.1'
|
|
@ -0,0 +1,23 @@
|
|||
"""
|
||||
sdk - vxGOS SDK entry
|
||||
"""
|
||||
import sys
|
||||
|
||||
from cli import cli_parse
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _main(argv):
|
||||
""" real entry """
|
||||
if argv[0].find('conv') == 0:
|
||||
return cli_parse(argv)
|
||||
print(f"not supported {argv[0]}", file=sys.stderr)
|
||||
return -1
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
sys.exit(_main(sys.argv[1:]))
|
|
@ -0,0 +1,63 @@
|
|||
"""
|
||||
cli.conv - assset conversion abstraction
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
|
||||
from cli.doctor import doctor_conv_cli
|
||||
from cli.assets import assets_conv_cli
|
||||
from cli.addin import addin_conv_cli
|
||||
|
||||
__all__ = [
|
||||
'__VXSDK_PLUGIN_META__',
|
||||
'cli_validate',
|
||||
'cli_parse',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
__VXSDK_PLUGIN_META__ = (
|
||||
['conv'],
|
||||
'vxGOS assets converter',
|
||||
"""vxsdk-conv
|
||||
Project assets converter
|
||||
|
||||
USAGE:
|
||||
vxsdk vxgos conv(-<ACTION>) [OPTIONS] ...
|
||||
|
||||
DESCRIPTION:
|
||||
Convert vxGOS project assets (or binary) into various form. By default, if
|
||||
no action is specified, the "assets" conversion is selected.
|
||||
|
||||
ACTIONS:
|
||||
asset convert asset into source file or binary file
|
||||
addin convert binary into addin file for vxOS
|
||||
doctor try to display assets and addin information (debug)
|
||||
|
||||
See `vxsdk vxgos conv-<action> --help` for more information on a specific
|
||||
action
|
||||
"""
|
||||
)
|
||||
|
||||
def cli_validate(name):
|
||||
""" validate the module name """
|
||||
return name.find('conv') == 0
|
||||
|
||||
def cli_parse(argv):
|
||||
""" Build subcommand entry """
|
||||
if '--help' in argv or '-h' in argv:
|
||||
log.user(__VXSDK_PLUGIN_META__[2])
|
||||
return 0
|
||||
if argv[0].find('conv-') != 0:
|
||||
argv[0] = 'conv-asset'
|
||||
action = argv[0][5:]
|
||||
if action == 'doctor':
|
||||
return doctor_conv_cli(argv[1:])
|
||||
if action == 'assets':
|
||||
return assets_conv_cli(argv[1:])
|
||||
if action == 'addin':
|
||||
return addin_conv_cli(argv[1:])
|
||||
log.error(f"unable to find action '{action}'")
|
||||
return 84
|
|
@ -0,0 +1,69 @@
|
|||
"""
|
||||
cli.conv.addin - vxSDK addin conversion
|
||||
"""
|
||||
import sys
|
||||
|
||||
#from core.conv.addin import generate_addin
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'addin_conv_cli'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
__HELP__ = """vxsdk-converter-addin
|
||||
Converte binary file into Vhex OS addin.
|
||||
|
||||
USAGE:
|
||||
vxsdk conv addin -b BINARY ...
|
||||
|
||||
DESCRIPTION:
|
||||
Convert a binary file into an application for the Vhex operating system.
|
||||
|
||||
OPTIONS:
|
||||
-b <binary path> ELF file (no check is performed in this file)
|
||||
-i <icon path> 92x62 pixel image path
|
||||
-o <output path> output path for the generated addin
|
||||
-n <internal name> internal addin name
|
||||
-v <internal version> internal addin version
|
||||
"""
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def addin_conv_cli(argv):
|
||||
"""Process CLI arguments"""
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__HELP__)
|
||||
sys.exit(0)
|
||||
|
||||
action = None
|
||||
info = [None, None, None, None, None]
|
||||
for arg in argv:
|
||||
if action == '-b':
|
||||
info[0] = arg
|
||||
if action == '-i':
|
||||
info[1] = arg
|
||||
if action == '-n':
|
||||
info[2] = arg
|
||||
if action == '-o':
|
||||
info[3] = arg
|
||||
if action == '-v':
|
||||
info[4] = arg
|
||||
if action:
|
||||
action = None
|
||||
continue
|
||||
if arg in ['-b', '-i', '-n', '-o', '-v']:
|
||||
action = arg
|
||||
continue
|
||||
|
||||
if not info[0]:
|
||||
log.error('converter: need binary path !')
|
||||
sys.exit(84)
|
||||
|
||||
log.error('not supported addin convertion')
|
||||
#return generate_addin(info[0], info[1], info[2], info[3], info[4])
|
|
@ -0,0 +1,101 @@
|
|||
"""
|
||||
cli.conv.assets - Vhex asset converter user interface
|
||||
"""
|
||||
import os
|
||||
import dataclasses
|
||||
|
||||
from core.logger import log
|
||||
from core import assets_generate
|
||||
|
||||
__all__ = [
|
||||
'assets_conv_cli'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
__HELP__ = """vxsdk-converter-asset
|
||||
Convert all assets file in the project directory.
|
||||
|
||||
USAGE:
|
||||
vxsdk vxgos conv-asset <assets prefix> <output prefix>
|
||||
|
||||
DESCRIPTION:
|
||||
Convert all assets file in the asset directory. This part of the converter
|
||||
module will scan the provided folder (or the current working directory) and
|
||||
will try to find all `vxconv.txt` file, which describe all assets that
|
||||
should be converted.
|
||||
|
||||
If no argument is provided, then the current working directory is used as
|
||||
asset prefix and a storag for all generated source file. You can modify
|
||||
this behaviour using OPTIONS.
|
||||
|
||||
For more information about the `vxconv.txt` in the wiki.
|
||||
|
||||
OPTIONS:
|
||||
-f, --force Force assets generation even if they exist
|
||||
--bootloader Generate bootloader font encoding
|
||||
--kernel Generate kernel font encoding
|
||||
--os Generate OS font encoding (default)
|
||||
--little-endian Encode assets information in little endian format
|
||||
--big-endian Encode assets information in big endian format
|
||||
-h, --help Display this help
|
||||
"""
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _ConvertAssetInfo():
|
||||
""" Assets converter information """
|
||||
force = False
|
||||
endianness = 'little'
|
||||
generator = 'os'
|
||||
prefix_output = ''
|
||||
prefix_assets = ''
|
||||
assets_filter = []
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def assets_conv_cli(argv):
|
||||
"""Process CLI arguments"""
|
||||
# check obvious flags
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__HELP__)
|
||||
return 0
|
||||
|
||||
# fetch user indication
|
||||
info = _ConvertAssetInfo()
|
||||
for arg in argv:
|
||||
if arg in ['-f', '--force']:
|
||||
info.force = True
|
||||
continue
|
||||
if arg == '--bootloader':
|
||||
info.generator = 'bootloader'
|
||||
continue
|
||||
if arg == '--little-endian':
|
||||
info.endianness = 'little'
|
||||
continue
|
||||
if arg == '--big-endian':
|
||||
info.endianness = 'big'
|
||||
continue
|
||||
if not info.prefix_assets:
|
||||
info.prefix_assets = arg
|
||||
continue
|
||||
if arg.find('--filter=') == 0:
|
||||
info.assets_filter = arg[9:].split(',')
|
||||
continue
|
||||
if info.prefix_output:
|
||||
log.warn(f"previous output path ({info.prefix_output}) dropped")
|
||||
info.prefix_output = arg
|
||||
|
||||
# check indication
|
||||
if not info.prefix_assets:
|
||||
log.emergency('missing assets prefix')
|
||||
if not info.prefix_assets:
|
||||
log.emergency('missing output prefix')
|
||||
info.prefix_assets = os.path.abspath(info.prefix_assets)
|
||||
info.prefix_output = os.path.abspath(info.prefix_output)
|
||||
|
||||
# generate assets information
|
||||
return assets_generate(info)
|
|
@ -0,0 +1,24 @@
|
|||
"""
|
||||
cli.conv.doctor - vxSDK converter doctor.
|
||||
"""
|
||||
import sys
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'doctor_conv_cli'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def doctor_conv_cli(_):
|
||||
"""Process CLI handling
|
||||
|
||||
TODO:
|
||||
> give asset file description to check error
|
||||
> try to display asset and addin information based on the project type
|
||||
"""
|
||||
log.warn('conv: doctor action not implemented yet')
|
||||
sys.exit(84)
|
|
@ -0,0 +1,4 @@
|
|||
"""
|
||||
core.conv - Vhex converter module
|
||||
"""
|
||||
from core.assets import assets_generate
|
|
@ -0,0 +1,67 @@
|
|||
#from core.conv.pixel import rgba8conv
|
||||
#
|
||||
#from PIL import Image
|
||||
#import os
|
||||
#
|
||||
#__all__ = [
|
||||
# 'generate_addin'
|
||||
#]
|
||||
#
|
||||
#
|
||||
#def generate_addin(binary, icon=None, name=None, output=None, version=None):
|
||||
# r"""Generate an addin for the Vhex Operating System.
|
||||
#
|
||||
# The addin name (passed through the `name` argument) is optional here. In
|
||||
# this case, the name will use the internal name...which can be guessed using
|
||||
# the binary name (e.i '/path/to/the/super_addin.elf' -> internal name =
|
||||
# 'super_addin' -> output name = '/path/to/the/super_addin').
|
||||
#
|
||||
# The output path for the generated file is, by defautl, the same path that
|
||||
# the binary but the suffix '.vxos' will be added.
|
||||
#
|
||||
# if the icon is not specified, a default blank icon will be used.
|
||||
#
|
||||
# Args:
|
||||
# > binary (str) - binary path
|
||||
# > icon (str) - addin icon path (optional)
|
||||
# > name (str) - addin name (displayed in the menu) (optional)
|
||||
# > output (str) - output path for the generated addin (optional)
|
||||
#
|
||||
# _fixme:
|
||||
# > generate default internal name
|
||||
# > change 8-bits icon into rgb565
|
||||
# > add internal addin version in the header
|
||||
# """
|
||||
# if not os.path.exists(binary):
|
||||
# logger(LOG_ERR, 'binary path is invalid')
|
||||
# sys.exit(84)
|
||||
# if icon and not os.path.exists(icon):
|
||||
# logger(LOG_WARN, f'{icon}: icon does not exists, ignored')
|
||||
# icon = None
|
||||
# if not name:
|
||||
# name = ''
|
||||
# if not output:
|
||||
# output = binary + '.vxos'
|
||||
#
|
||||
# if icon:
|
||||
# bitmap = Image.open(icon)
|
||||
# if bitmap.size != (92, 64):
|
||||
# logger(
|
||||
# LOG_ERR,
|
||||
# f'{icon}:icon size does not match {bitmap.size} != (92, 64)',
|
||||
# exit=84
|
||||
# )
|
||||
#
|
||||
# with open(binary, 'rb') as b:
|
||||
# with open(output, 'wb') as a:
|
||||
# a.write(b'VHEX')
|
||||
# a.write(name.encode('utf8'))
|
||||
# a.write(b'\x00')
|
||||
# if icon:
|
||||
# for pixel in bitmap.getdata():
|
||||
# a.write(rgba8conv(pixel).to_bytes(1, 'big'))
|
||||
# else:
|
||||
# a.write(bytes(92*64))
|
||||
# a.write(b.read())
|
||||
#
|
||||
# return 0
|
|
@ -0,0 +1,150 @@
|
|||
"""
|
||||
core.conv.assets - Vhex assets converter
|
||||
"""
|
||||
import os
|
||||
import toml
|
||||
|
||||
from core.logger import log
|
||||
from core.font import font_generate
|
||||
from core.image import image_generate
|
||||
|
||||
__all__ = [
|
||||
'assets_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
class _VxAssetException(Exception):
|
||||
""" simple exception wrapper """
|
||||
|
||||
class _VxAsset():
|
||||
"""Represent a asset object
|
||||
|
||||
This is an internal class which represents assets information with some
|
||||
methods to abstract conversion and file type manipulation (for asset type
|
||||
font and asset type bitmap).
|
||||
|
||||
Also note that this class is private because we use a tricky optimization
|
||||
to parse the `vxconv.txt` file, this is why we have no "private" property
|
||||
with setter and getter, and why this class is "hidden".
|
||||
|
||||
Some important methods to note:
|
||||
|
||||
================================== =======================================
|
||||
Name Description
|
||||
================================== =======================================
|
||||
generate() Generate the source file (C)
|
||||
================================== =======================================
|
||||
|
||||
"""
|
||||
def __init__(self, prefix, name, meta):
|
||||
if 'path' not in meta:
|
||||
raise _VxAssetException(f"[{name}] missing path information")
|
||||
if 'type' not in meta:
|
||||
raise _VxAssetException(f"[{name}] missing type information")
|
||||
if meta['type'] not in ['font', 'image']:
|
||||
raise _VxAssetException(f"asset type '{meta[type]}' is not known")
|
||||
|
||||
self._name = name
|
||||
self._meta = meta
|
||||
self._type = meta['type']
|
||||
self._path = prefix + '/' + meta['path']
|
||||
if not os.path.exists(self.path):
|
||||
raise _VxAssetException(
|
||||
"asset path '{self._path}' cannot be openned"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<_VxAssetObj, {self.name}>'
|
||||
|
||||
def __str__(self):
|
||||
content = f"[{self.name}]\n"
|
||||
content += f" - type: {self.type}\n"
|
||||
content += f" - path: {self.path}\n"
|
||||
return content
|
||||
|
||||
#---
|
||||
# Getter
|
||||
#---
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""<property> path"""
|
||||
return self._path
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""<property> name"""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""<property> type"""
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
"""<property> meta"""
|
||||
return self._meta
|
||||
|
||||
#---
|
||||
# Public method
|
||||
#---
|
||||
|
||||
def generate_source_file(
|
||||
self,
|
||||
prefix_output,
|
||||
generator,
|
||||
endianness,
|
||||
force_generate
|
||||
):
|
||||
"""generate source file """
|
||||
function = font_generate if self.type == 'font' else image_generate
|
||||
return function(
|
||||
self,
|
||||
prefix_output,
|
||||
generator,
|
||||
endianness,
|
||||
force_generate
|
||||
)
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def assets_generate(info):
|
||||
""" Walk through the assets prefix and generate all source file
|
||||
|
||||
@args
|
||||
> info (dataclass) - contains all information about the conversion
|
||||
|
||||
@return
|
||||
> a list of all generated sources pathname
|
||||
"""
|
||||
if not os.path.exists(info.prefix_output):
|
||||
os.makedirs(info.prefix_output)
|
||||
for root, _, files in os.walk(info.prefix_assets):
|
||||
if 'vxconv.toml' not in files:
|
||||
continue
|
||||
with open(f"{root}/vxconv.toml", 'r', encoding='utf-8') as inf:
|
||||
content = toml.loads(inf.read())
|
||||
for asset_name in content:
|
||||
if info.assets_filter:
|
||||
if asset_name not in info.assets_filter:
|
||||
continue
|
||||
log.debug(f"converting {asset_name}...")
|
||||
log.user(
|
||||
_VxAsset(
|
||||
root,
|
||||
asset_name,
|
||||
content[asset_name]
|
||||
).generate_source_file(
|
||||
info.prefix_output,
|
||||
info.generator,
|
||||
info.endianness,
|
||||
info.force
|
||||
)
|
||||
)
|
||||
return 0
|
|
@ -0,0 +1,49 @@
|
|||
"""
|
||||
core.conv.type.font - Vhex font converter
|
||||
"""
|
||||
import os
|
||||
|
||||
from core.logger import log
|
||||
from core.font.meta import font_meta_fetch
|
||||
from core.font.convert import font_convert
|
||||
from core.font.generator import font_generate_source_file
|
||||
|
||||
__all__ = [
|
||||
'font_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def font_generate(asset, prefix_output, generator, endian, force_generate):
|
||||
""" Convert an image asset to a C source file
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - minimal asset information
|
||||
> prefix_output (str) - prefix for source file generation
|
||||
> generator (str) - selected generator for C file content
|
||||
> endian (str) - selected endianness encoding
|
||||
> force_generate (bool) - force generate the source file
|
||||
|
||||
@return
|
||||
> pathname of the generated file
|
||||
"""
|
||||
# check if the asset already exists
|
||||
asset_src = f'{prefix_output}/{asset.name}_vxfont.c'
|
||||
if not force_generate and os.path.exists(asset_src):
|
||||
log.debug(f"{asset.name}: not generated, cached")
|
||||
return asset_src
|
||||
|
||||
# generate font information
|
||||
if not (font_info := font_meta_fetch(asset)):
|
||||
return ''
|
||||
if font_convert(asset, font_info) != 0:
|
||||
return ''
|
||||
|
||||
# create the source file
|
||||
content = font_generate_source_file(asset, font_info, generator, endian)
|
||||
with open(asset_src, "w", encoding='utf8') as file:
|
||||
file.write(content)
|
||||
log.debug(f"source file generated at {asset_src}")
|
||||
return asset_src
|
|
@ -0,0 +1,200 @@
|
|||
"""
|
||||
core.font.convert - common conversion handling
|
||||
"""
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from core.logger import log
|
||||
from core.font.glyph import glyph_get_wgeometry, glyph_encode
|
||||
|
||||
__all__ = [
|
||||
'font_convert',
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _font_convert_proportional(packed_info):
|
||||
""" Generate proportional font
|
||||
|
||||
Proportional font means that each character have its own width size (but
|
||||
have a common height). We need to performs more complexe handling than the
|
||||
monospaced one.
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> font_information (dict) - font indication
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
# unpack information
|
||||
font_info = packed_info[0]
|
||||
img_info = packed_info[1]
|
||||
glyph_info = packed_info[2]
|
||||
data_info = packed_info[4]
|
||||
geometry_info = packed_info[5]
|
||||
|
||||
# isolate needed information
|
||||
img = img_info['obj']
|
||||
img_raw = img_info['raw']
|
||||
nb_col = packed_info[3][0]
|
||||
nb_row = packed_info[3][1]
|
||||
gwidth = glyph_info[0]
|
||||
gheight = glyph_info[1]
|
||||
|
||||
# main loop, walk glyph per glyph
|
||||
_py = (font_info['grid_border'] + font_info['grid_padding']) * img.size[0]
|
||||
for _ in range(0, nb_row):
|
||||
_px = font_info['grid_border'] + font_info['grid_padding']
|
||||
for _ in range(0, nb_col):
|
||||
# generate width geometry information
|
||||
glyph_get_wgeometry(
|
||||
geometry_info,
|
||||
img_raw,
|
||||
img.size,
|
||||
(_px, _py),
|
||||
(font_info['grid_size_x'], font_info['grid_size_y'])
|
||||
)
|
||||
|
||||
# save critical glyph geometry information that will be encoded in
|
||||
# the final C source file
|
||||
font_info['glyph_props'].append((
|
||||
geometry_info['wend'] - geometry_info['wstart'],
|
||||
data_info['idx'],
|
||||
data_info['shift']
|
||||
))
|
||||
|
||||
# encode glyph information
|
||||
glyph_encode(data_info, img_info, geometry_info, _px, _py)
|
||||
|
||||
# update loop information
|
||||
font_info['glyph_count'] += 1
|
||||
_px += gwidth
|
||||
_py += gheight * img.size[0]
|
||||
return 0
|
||||
|
||||
def _font_convert_monospaced(packed_info):
|
||||
""" Generate proportional font
|
||||
|
||||
Proportional font means that each character have its own width size (but
|
||||
have a common height). We need to performs more complexe handling than the
|
||||
monospaced one.
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> font_information (dict) - font indication
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
# unpack information
|
||||
font_info = packed_info[0]
|
||||
img_info = packed_info[1]
|
||||
glyph_info = packed_info[2]
|
||||
grid_info = packed_info[3]
|
||||
data_info = packed_info[4]
|
||||
geometry_info = packed_info[5]
|
||||
|
||||
# isolate needed information
|
||||
img = img_info['obj']
|
||||
nb_row = grid_info[1]
|
||||
nb_col = grid_info[0]
|
||||
gwidth = glyph_info[0]
|
||||
gheight = glyph_info[1]
|
||||
|
||||
# main loop, walk glyph per glyph
|
||||
_py = (font_info['grid_border'] + font_info['grid_padding']) * img.size[0]
|
||||
for _ in range(0, nb_row):
|
||||
_px = font_info['grid_border'] + font_info['grid_padding']
|
||||
for _ in range(0, nb_col):
|
||||
glyph_encode(data_info, img_info, geometry_info, _px, _py)
|
||||
font_info['glyph_count'] += 1
|
||||
_px += gwidth
|
||||
_py += gheight * img.size[0]
|
||||
return 0
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def font_convert(asset, font_info):
|
||||
""" Generate font information
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> font_info (dict) - font information
|
||||
|
||||
@return
|
||||
> 0 if success, negative value otherwise
|
||||
"""
|
||||
# generate image information
|
||||
img = Image.open(asset.path)
|
||||
img_raw = img.getdata()
|
||||
img_info = {
|
||||
'obj' : img,
|
||||
'raw' : img_raw
|
||||
}
|
||||
|
||||
# pre-calculate the "real" glyph width and height using padding information
|
||||
glyph_info = [0, 0]
|
||||
glyph_info[0] = font_info['grid_size_x'] + font_info['grid_padding']
|
||||
glyph_info[1] = font_info['grid_size_y'] + font_info['grid_padding']
|
||||
gheight = glyph_info[1]
|
||||
gwidth = glyph_info[0]
|
||||
log.debug(f"gwidth = {gwidth} && gheight = {gheight}")
|
||||
|
||||
# pre-calculate the number of row and column of the font
|
||||
grid_info = [0, 0]
|
||||
grid_info[0] = int((img.size[0] - (font_info['grid_border'] * 2)) / gwidth)
|
||||
grid_info[1] = int((img.size[1] - (font_info['grid_border'] * 2)) /gheight)
|
||||
nb_col = grid_info[0]
|
||||
nb_row = grid_info[1]
|
||||
log.debug(f"nb_row = {nb_row} && nb_col = {nb_col}")
|
||||
|
||||
# pre-calculate and prepare per-glyph information
|
||||
# @note
|
||||
# The generated data is designed for 4-alignement padding. This to have
|
||||
# speed-up on drawing function.
|
||||
font_info['glyph_size'] = font_info['grid_size_x']
|
||||
font_info['glyph_size'] *= font_info['grid_size_y']
|
||||
font_info['font_size'] = font_info['glyph_size'] * nb_row * nb_col
|
||||
font_info['glyph_count'] = 0
|
||||
font_info['glyph_props'] = []
|
||||
font_info['data'] = [0] * int((font_info['font_size'] + 31) / 32)
|
||||
log.debug(f"data original = {id(font_info['data'])}")
|
||||
|
||||
# generate data information
|
||||
data_info = {
|
||||
'table' : font_info['data'],
|
||||
'idx' : 0,
|
||||
'shift' : 0
|
||||
}
|
||||
log.debug(f"data packed = {id(data_info['table'])}")
|
||||
|
||||
# generate geometry information
|
||||
geometry_info = {
|
||||
'hstart' : 0,
|
||||
'hend' : font_info['grid_size_y'],
|
||||
'wstart' : 0,
|
||||
'wend' : font_info['grid_size_x'],
|
||||
}
|
||||
|
||||
# select the converter
|
||||
converter = _font_convert_monospaced
|
||||
if font_info['is_proportional']:
|
||||
converter = _font_convert_proportional
|
||||
|
||||
# convert font
|
||||
converter((
|
||||
font_info,
|
||||
img_info,
|
||||
glyph_info,
|
||||
grid_info,
|
||||
data_info,
|
||||
geometry_info
|
||||
))
|
||||
|
||||
log.debug(f"data packed end = {id(data_info['table'])}")
|
||||
return 0
|
|
@ -0,0 +1,35 @@
|
|||
"""
|
||||
core.font.generator - generator abstraction
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
from core.font.generator.bootloader import font_generate_bootloader_source_file
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def font_generate_source_file(asset, font_info, generator, endianness):
|
||||
""" return the C source file based on the selected generator
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - minimal asset information
|
||||
> font_info (dict) - font information
|
||||
> generator (str) - selected generator for C file content
|
||||
> endianness (str) - selected endianness encoding
|
||||
|
||||
@return
|
||||
> file content (str)
|
||||
"""
|
||||
if generator not in ['bootloader', 'kernel', 'os']:
|
||||
log.emergency(f"not supported generator '{generator}'")
|
||||
|
||||
if generator == 'bootloader':
|
||||
return font_generate_bootloader_source_file(
|
||||
asset,
|
||||
font_info,
|
||||
endianness
|
||||
)
|
||||
|
||||
log.emergency(f"generator '{generator}' not implemented yet o(x_x)o")
|
||||
return ''
|
|
@ -0,0 +1,102 @@
|
|||
"""
|
||||
core.font.generator.bootloader - bootloader font generator
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'font_generate_bootloader_source_file'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _u32_conv_little_endian(x32):
|
||||
# return ((x32 & 0xff000000) >> 24) \
|
||||
# | ((x32 & 0x00ff0000) >> 8) \
|
||||
# | ((x32 & 0x0000ff00) << 8) \
|
||||
# | ((x32 & 0x000000ff) << 24)
|
||||
return x32
|
||||
|
||||
def _u32_conv_big_endian(x32):
|
||||
return x32
|
||||
|
||||
def _font_generate_normal_source(asset, font_info, endianness):
|
||||
""" Print chaset is a image file
|
||||
|
||||
Generate a C font file content based on bootloader font internal
|
||||
structure and header declaration.
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> info (dict) - hold font information
|
||||
> endianness (str) - selected endianness encoding
|
||||
|
||||
@return
|
||||
> complet font file content
|
||||
"""
|
||||
# generate basic header
|
||||
content = '#include "bootloader/display.h"\n'
|
||||
content += '\n'
|
||||
content += f"/* {asset.name} - Vhex asset\n"
|
||||
content += ' This object has been converted by using the vxSDK '
|
||||
content += 'converter */\n'
|
||||
content += f"struct font const {asset.name} = " + "{\n"
|
||||
|
||||
# handle endianness
|
||||
u32 = _u32_conv_little_endian
|
||||
if endianness != 'little':
|
||||
u32 = _u32_conv_big_endian
|
||||
|
||||
# encode font bitmap
|
||||
line = 0
|
||||
log.debug(f"data = {font_info['data']}")
|
||||
content += " .data = (uint32_t[]){\n"
|
||||
for pixel in font_info['data']:
|
||||
if line == 0:
|
||||
content += ' '
|
||||
if line >= 1:
|
||||
content += ' '
|
||||
content += f"{u32(pixel):#010x},"
|
||||
if (line := line + 1) == 6:
|
||||
content += '\n'
|
||||
line = 0
|
||||
if line != 0:
|
||||
content += '\n'
|
||||
content += ' },\n'
|
||||
|
||||
# indicate other font information
|
||||
content += f" .count = {font_info['glyph_count']},\n"
|
||||
content += f" .height = {font_info['glyph_height']},\n"
|
||||
content += f" .width = {font_info['grid_size_x']},\n"
|
||||
content += f" .line_height = {font_info['line_height']},\n"
|
||||
content += f" .char_block_size = {font_info['glyph_size']},\n"
|
||||
|
||||
# closure and return
|
||||
content += '};\n'
|
||||
return content
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def font_generate_bootloader_source_file(asset, font_info, endianness):
|
||||
""" Generate font source file content
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> info (dict) - hold font information
|
||||
> endianness (str) - selected endianness encoding
|
||||
|
||||
@return
|
||||
> file C content string
|
||||
"""
|
||||
# check font validity (common vxconv.toml parser)
|
||||
if font_info['is_proportional']:
|
||||
log.emergency(f"{asset.name}: unsupported proportional font")
|
||||
if font_info['charset'] == 'unicode':
|
||||
log.emergency(f"{asset.name}: unsupported unicode font")
|
||||
|
||||
# generate file content
|
||||
return _font_generate_normal_source(asset, font_info, endianness)
|
|
@ -0,0 +1,105 @@
|
|||
"""
|
||||
core.font.generator.kernel - kernel font generator
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def _font_generate_unicode_source(_):
|
||||
""" Unicode special chaset directory """
|
||||
log.error("unicode conversion not implemented yet o(x_x)o")
|
||||
return ''
|
||||
|
||||
def _font_generate_normal_source(font_info):
|
||||
""" Print chaset is a image file
|
||||
"""
|
||||
content = "\t.glyph = {\n"
|
||||
content += f"\t\t.height = {font_info['glyph_height']},\n"
|
||||
content += f"\t\t.line_height = {font_info['line_height']},\n"
|
||||
|
||||
# encode font bitmap
|
||||
line = 0
|
||||
log.debug(f"data = {font_info['data']}")
|
||||
content += "\t\t.data = (uint32_t[]){\n"
|
||||
for pixel in font_info['data']:
|
||||
if line == 0:
|
||||
content += '\t\t\t'
|
||||
if line >= 1:
|
||||
content += ' '
|
||||
content += f"{pixel:#010x},"
|
||||
if (line := line + 1) == 4:
|
||||
content += '\n'
|
||||
line = 0
|
||||
if line != 0:
|
||||
content += '\n'
|
||||
content += '\t\t},\n'
|
||||
|
||||
# indicate the number of glyph in the bitmap
|
||||
content += f"\t\t.count = {font_info['glyph_count']},\n"
|
||||
|
||||
# encode proportional information if needed
|
||||
if font_info['is_proportional']:
|
||||
content += '\t\t.prop = (struct __workaround[]){\n'
|
||||
for prop in font_info['glyph_props']:
|
||||
content += "\t\t\t{\n"
|
||||
content += f"\t\t\t\t.width = {prop[0]},\n"
|
||||
content += f"\t\t\t\t.index = {prop[1]},\n"
|
||||
content += f"\t\t\t\t.shift = {prop[2]},\n"
|
||||
content += "\t\t\t},\n"
|
||||
else:
|
||||
content += "\t\t.mono = {,\n"
|
||||
content += f"\t\t\t.width = {font_info['glyph_width']},\n"
|
||||
content += f"\t\t\t.size = {font_info['glyph_size']},\n"
|
||||
content += "\t\t},\n"
|
||||
content += "\t},\n"
|
||||
|
||||
# skip unicode struct
|
||||
content += "\t.unicode = {\n"
|
||||
content += "\t\t.blocks = NULL,\n"
|
||||
content += "\t\t.block_count = 0,\n"
|
||||
content += "\t}\n"
|
||||
return content
|
||||
|
||||
def _font_generate_source_file(asset, font_info):
|
||||
""" Generate font source file content
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> info (dict) - hold font information
|
||||
|
||||
@return
|
||||
> file C content string
|
||||
"""
|
||||
# generate basic header
|
||||
content = "#include <vhex/display/font.h>\n"
|
||||
content += "\n"
|
||||
content += f"/* {asset.name} - Vhex asset\n"
|
||||
content += " This object has been converted by using the vxSDK "
|
||||
content += "converter */\n"
|
||||
content += f"struct font const {asset.name} = " + "{\n"
|
||||
content += f"\t.name = \"{asset.name}\",\n"
|
||||
|
||||
# shape information
|
||||
content += "\t.shape = {\n"
|
||||
content += "\t\t.bold = 0,\n"
|
||||
content += "\t\t.italic = 0,\n"
|
||||
content += "\t\t.serif = 0,\n"
|
||||
content += "\t\t.mono = 0,\n"
|
||||
content += f"\t\t.prop = {int(font_info['is_proportional'])},\n"
|
||||
content += "\t},\n"
|
||||
|
||||
# manage display indication
|
||||
content += f"\t.char_spacing = {font_info['char_spacing']},\n"
|
||||
|
||||
# handle special charset behaviour
|
||||
if font_info['charset'] == 'unicode':
|
||||
content += _font_generate_unicode_source(font_info)
|
||||
else:
|
||||
content += _font_generate_normal_source(font_info)
|
||||
|
||||
# closure and return
|
||||
content += '};\n'
|
||||
return content
|
|
@ -0,0 +1,89 @@
|
|||
"""
|
||||
core.font.glyph - glyph abstraction
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'glyph_get_wgeometry',
|
||||
'glyph_encode',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def glyph_get_wgeometry(geometry_info, img_raw, img_size, pos, grid_size):
|
||||
""" Generate glyph width geometry information
|
||||
|
||||
@args
|
||||
> geometry_info (dict) - geometry information
|
||||
> img_raw (list) - list of all pixel of the image
|
||||
> img_size (tuple) - image width and image height
|
||||
> pos (tuple) - glyph position information (X and Y in pixel)
|
||||
> grid_size (tuple) - glyph grid size information (width and height)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
geometry_info['wstart'] = -1
|
||||
geometry_info['wend'] = -1
|
||||
|
||||
_px = pos[0]
|
||||
_py = pos[1]
|
||||
log.debug(f'[geometry] X:{pos[0]} Y:{int(pos[1]/img_size[0])}')
|
||||
log.debug(f' - grid_size = {grid_size}')
|
||||
for _ in range(0, grid_size[1]):
|
||||
for offx in range(0, grid_size[0]):
|
||||
if img_raw[_py + (_px + offx)][:3] == (255, 255, 255):
|
||||
continue
|
||||
if geometry_info['wstart'] < 0 or offx < geometry_info['wstart']:
|
||||
geometry_info['wstart'] = offx
|
||||
if geometry_info['wstart'] < 0 or offx > geometry_info['wend']:
|
||||
geometry_info['wend'] = offx
|
||||
_py += img_size[0]
|
||||
geometry_info['wend'] += 1
|
||||
log.debug(f' - geometry = {geometry_info}')
|
||||
|
||||
def glyph_encode(data_info, img_info, geometry, posx, posy):
|
||||
""" Encode glyph bitmap
|
||||
|
||||
@args
|
||||
> data_info (dict) - internal data information (list, index and shift)
|
||||
> img_info (dict) - image-related information (object and raw content)
|
||||
> geometry (dict) - geometry information
|
||||
> posx (int) - X-axis position in pixel
|
||||
> posy (int) - Y-axis position in pixel
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch information
|
||||
img = img_info['obj']
|
||||
img_raw = img_info['raw']
|
||||
data = data_info['table']
|
||||
data_idx = data_info['idx']
|
||||
data_shift = data_info['shift']
|
||||
wstart = geometry['wstart']
|
||||
wend = geometry['wend']
|
||||
|
||||
# encode the glyph
|
||||
yoff = 0
|
||||
log.debug(f'[encode] X:{posx} Y:{int(posy/img.size[0])}')
|
||||
for _h in range(geometry['hstart'], geometry['hend']):
|
||||
for _w in range(wstart, wend):
|
||||
if img_raw[(posy + yoff) + (posx + _w)][:3] == (0, 0, 0):
|
||||
log.debug('#', end='')
|
||||
data[data_idx] |= 0x80000000 >> data_shift
|
||||
else:
|
||||
log.debug('.', end='')
|
||||
data[data_idx] &= ~(0x80000000 >> data_shift)
|
||||
if (data_shift := data_shift + 1) >= 32:
|
||||
data_shift = 0
|
||||
data_idx += 1
|
||||
log.debug('')
|
||||
yoff += img.size[0]
|
||||
|
||||
# commit modification
|
||||
data_info['idx'] = data_idx
|
||||
data_info['shift'] = data_shift
|
|
@ -0,0 +1,72 @@
|
|||
"""
|
||||
core.font.meta - TOML meta information file abstraction
|
||||
"""
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'font_meta_fetch',
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def font_meta_fetch(asset):
|
||||
""" Check and fetch font information
|
||||
|
||||
@arg
|
||||
> asset (VxAsset) - asset information
|
||||
|
||||
@return
|
||||
> dictionary with font information
|
||||
"""
|
||||
# generate font default information
|
||||
font_info = {
|
||||
# user can customise
|
||||
'charset' : 'normal',
|
||||
'grid_size_x' : 0,
|
||||
'grid_size_y' : 0,
|
||||
'grid_padding' : 1,
|
||||
'grid_border' : 0,
|
||||
'is_proportional' : False,
|
||||
'line_height' : 0,
|
||||
'char_spacing' : 1,
|
||||
|
||||
# generated "on-the-fly" by the conversion step
|
||||
# @notes
|
||||
# This is mainly to provide cache for the Vhex operating system to
|
||||
# speed-up render calculation by avoiding recurent caculation.
|
||||
'glyph_size' : 0,
|
||||
'glyph_height' : 0,
|
||||
'font_size' : 0,
|
||||
'data' : []
|
||||
}
|
||||
|
||||
# handle user meta-indication
|
||||
if 'charset' in asset.meta:
|
||||
if asset.meta['charset'] not in ['default', 'unicode']:
|
||||
log.error(f"Unknown charset '{asset.meta['charset']}', abord")
|
||||
return None
|
||||
font_info['charset'] = asset.meta['charset']
|
||||
if 'grid_size' not in asset.meta:
|
||||
log.error("Missing critical grid size information, abord")
|
||||
return None
|
||||
grid_size = asset.meta['grid_size'].split('x')
|
||||
font_info['grid_size_x'] = int(grid_size[0])
|
||||
font_info['grid_size_y'] = int(grid_size[1])
|
||||
if 'grid_padding' in asset.meta:
|
||||
font_info['grid_padding'] = int(asset.meta['grid_padding'])
|
||||
if 'grid_border' in asset.meta:
|
||||
font_info['grid_border'] = int(asset.meta['grid_border'])
|
||||
if 'proportional' in asset.meta:
|
||||
font_info['is_proportional'] = asset.meta['proportional']
|
||||
font_info['line_height'] = font_info['grid_size_y']
|
||||
if 'line_height' in asset.meta:
|
||||
font_info['line_height'] = asset.meta['line_height']
|
||||
if 'char_spacing' in asset.meta:
|
||||
font_info['char_spacing'] = asset.meta['char_spacing']
|
||||
font_info['glyph_height'] = font_info['grid_size_y']
|
||||
|
||||
# return font information
|
||||
return font_info
|
|
@ -0,0 +1,403 @@
|
|||
"""
|
||||
core.conv.type.image - Vhex image converter
|
||||
"""
|
||||
import os
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from core.logger import log
|
||||
from core.pixel import pixel_rgb24to16
|
||||
|
||||
__all__ = [
|
||||
'image_generate'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
## Profile handling
|
||||
|
||||
def _profile_gen(profile, name, palette=None, alpha=None):
|
||||
r""" Internal image profile class
|
||||
|
||||
================================== ========================================
|
||||
Property Description
|
||||
================================== ========================================
|
||||
id (int) profile ID
|
||||
names (array of str) list all profile names
|
||||
format (str) profile format name (vhex API)
|
||||
has_alpha (bool) indicate if the profil has alpha
|
||||
alpha (int) alpha index in the palette (or mask)
|
||||
is_indexed (bool) indicate if it should be indexed
|
||||
palette_base (int) indicate base index for color inserting
|
||||
palette_color_count (int) indicate the number of color (palette)
|
||||
palette_trim (bool) indicate if the palette should be trimed
|
||||
================================== ========================================
|
||||
"""
|
||||
profile = {
|
||||
'profile' : profile,
|
||||
'name' : name,
|
||||
'has_alpha' : (alpha is not None),
|
||||
'alpha' : alpha,
|
||||
'is_indexed': (palette is not None),
|
||||
'palette' : None
|
||||
}
|
||||
if palette is not None:
|
||||
profile['palette_base'] = palette[0]
|
||||
profile['palette_color_count'] = palette[1]
|
||||
profile['palette_trim'] = palette[2]
|
||||
return profile
|
||||
|
||||
# all supported profile information
|
||||
VX_PROFILES = [
|
||||
_profile_gen('IMAGE_RGB565', "p16"),
|
||||
_profile_gen('IMAGE_RGB565A', "p16a", alpha=0x0001),
|
||||
_profile_gen('IMAGE_P8_RGB565', "p8", palette=(0,256,True)),
|
||||
_profile_gen('IMAGE_P8_RGB565A', "p8a", palette=(1,256,True), alpha=0),
|
||||
_profile_gen('IMAGE_P4_RGB565', "p4", palette=(0,16,False)),
|
||||
_profile_gen('IMAGE_P4_RGB565A', "p4a", palette=(1,16,False), alpha=0),
|
||||
]
|
||||
|
||||
def _profile_find(name):
|
||||
"""Find a profile by name."""
|
||||
for profile in VX_PROFILES:
|
||||
if name == profile['name']:
|
||||
return profile
|
||||
return None
|
||||
|
||||
## Image manipulation
|
||||
|
||||
def _image_isolate_alpha(info):
|
||||
""" Isolate alpha corlor of the image
|
||||
|
||||
Vhex use a particular handling for alpha color and this information should
|
||||
use a strict encoding way. Things that Pillow don't do properly. So, lets
|
||||
manually setup our alpha isolation and patch Pillow alpha palette handling.
|
||||
|
||||
@args
|
||||
> info (dict) - contains all needed information (image, data, ...)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch needed information
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
|
||||
# Save the alpha channel and make it 1-bit. We need to do this because
|
||||
# the alpha value is handled specialy in Vhex and the image conversion
|
||||
# to palette-oriented image is weird : the alpha colors is also converted
|
||||
# in the palette
|
||||
if profile['has_alpha']:
|
||||
alpha_channel = img.getchannel("A").convert("1", dither=Image.NONE)
|
||||
else:
|
||||
alpha_channel = Image.new("1", img.size, 1)
|
||||
|
||||
alpha_pixels = alpha_channel.load()
|
||||
img = img.convert("RGB")
|
||||
|
||||
# Transparent pixels have random values on the RGB channels, causing
|
||||
# them to use up palette entries during quantization. To avoid that, set
|
||||
# their RGB data to a color used somewhere else in the image.
|
||||
pixels = img.load()
|
||||
bg_color = next(
|
||||
(
|
||||
pixels[x,y]
|
||||
for x in range(img.width)
|
||||
for y in range(img.height)
|
||||
if alpha_pixels[x,y] > 0
|
||||
),
|
||||
(0,0,0)
|
||||
)
|
||||
for _y in range(img.height):
|
||||
for _x in range(img.width):
|
||||
if alpha_pixels[_x, _y] == 0:
|
||||
pixels[_x, _y] = bg_color
|
||||
|
||||
# update external information
|
||||
info['img'] = img
|
||||
info['img_pixel_list_alpha'] = alpha_pixels
|
||||
info['img_pixel_list_clean'] = pixels
|
||||
|
||||
def _image_encode_palette(info):
|
||||
""" Generate palette information
|
||||
|
||||
This routine is involved only if the targeted profile is indexed. We need
|
||||
to generate (and isolate) color palette.
|
||||
|
||||
@args
|
||||
> info (dict) - contains all needed information (image, data, ...)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch needed information
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
|
||||
# convert image into palette format
|
||||
# note: we remove one color slot in the palette for the alpha one
|
||||
color_count = profile['palette_color_count'] - int(profile['has_alpha'])
|
||||
img = img.convert(
|
||||
'P',
|
||||
dither=Image.NONE,
|
||||
palette=Image.ADAPTIVE,
|
||||
colors=color_count
|
||||
)
|
||||
|
||||
# The palette format is a list of N triplets ([r, g, b, ...]). But,
|
||||
# sometimes, colors after img.convert() are not numbered 0 to
|
||||
# `color_count`, because the palette don't need to be that big. So,
|
||||
# we calculate the "palette size" by walking throuth the bitmap and
|
||||
# by saving the biggest index used.
|
||||
pixels = img.load()
|
||||
nb_triplet = 1 + max(
|
||||
pixels[x,y]
|
||||
for y in range(img.height)
|
||||
for x in range(img.width)
|
||||
)
|
||||
palette = img.getpalette()[:3 * nb_triplet]
|
||||
palette = list(zip(palette[::3], palette[1::3], palette[2::3]))
|
||||
|
||||
# For formats with transparency, add an "unused" palette slot which
|
||||
# will used has pink/purple in case of a bad application try to use
|
||||
# this value anyway
|
||||
if profile['has_alpha']:
|
||||
palette = [(255, 0, 255)] + palette
|
||||
nb_triplet += 1
|
||||
|
||||
# Also keep track of how to remap indices from the values generated
|
||||
# by img.convert() into the palette, which is shifted by 1 due to
|
||||
# alpha and also starts at profile.palette_base.
|
||||
#
|
||||
# Note: profile.palette_base already starts 1 value later for
|
||||
# formats with alpha.
|
||||
palette_map = [
|
||||
(profile['palette_base'] + i) % profile['palette_color_count']
|
||||
for i in range(nb_triplet)
|
||||
]
|
||||
|
||||
# Encode the palette
|
||||
palette_color_count = nb_triplet
|
||||
if not profile['palette_trim']:
|
||||
palette_color_count = profile['palette_color_count']
|
||||
|
||||
palette_data = [0] * palette_color_count
|
||||
for i, rgb24 in enumerate(palette):
|
||||
palette_data[i] = pixel_rgb24to16(rgb24)
|
||||
|
||||
# update internal information
|
||||
info['palette_map'] = palette_map
|
||||
info['palette_data'] = palette_data
|
||||
info['palette_color_count'] = palette_color_count
|
||||
info['nb_triplet'] = nb_triplet
|
||||
info['img_pixel_list_clean'] = pixels
|
||||
|
||||
def _image_encode_bitmap(info):
|
||||
""" Encode the bitmap
|
||||
|
||||
This routine will generate the main data list which will contains the
|
||||
bitmap using Vhex-specific encoding.
|
||||
|
||||
@args
|
||||
> info (dict) - contains all needed information (image, data, ...)
|
||||
|
||||
@return
|
||||
> Nothing
|
||||
"""
|
||||
# fetch needed information
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
alpha_pixels = info['img_pixel_list_alpha']
|
||||
pixels = info['img_pixel_list_clean']
|
||||
palette_map = info['palette_map']
|
||||
|
||||
# generate profile-specific geometry information
|
||||
if profile['name'] in ['p16', 'p16a']:
|
||||
# Preserve alignment between rows by padding to 4 bytes
|
||||
nb_stride = ((img.width + 1) // 2) * 4
|
||||
data_size = (nb_stride * img.height) * 2
|
||||
elif profile['name'] in ['p8', 'p8a']:
|
||||
nb_stride = img.width
|
||||
data_size = img.width * img.height
|
||||
else:
|
||||
# Pad whole bytes
|
||||
nb_stride = (img.width + 1) // 2
|
||||
data_size = nb_stride * img.height
|
||||
|
||||
# Generate the real data map
|
||||
data = [0] * data_size
|
||||
|
||||
# encode the bitmap
|
||||
for _y in range(img.height):
|
||||
for _x in range(img.width):
|
||||
# get alpha information about this pixel
|
||||
_a = alpha_pixels[_x, _y]
|
||||
|
||||
if profile['name'] in ['p16', 'p16a']:
|
||||
# If c lands on the alpha value, flip its lowest bit to avoid
|
||||
# ambiguity with alpha
|
||||
_c = profile['alpha']
|
||||
if not _a:
|
||||
_c = pixel_rgb24to16(pixels[_x, _y]) & ~1
|
||||
data[(img.width * _y) + _x] = _c
|
||||
|
||||
elif profile['name'] in ['p8', 'p8a']:
|
||||
_c = palette_map[pixels[_x,_y]] if _a > 0 else profile['alpha']
|
||||
data[(img.width * _y) + _x] = _c
|
||||
|
||||
else:
|
||||
_c = palette_map[pixels[_x,_y]] if _a > 0 else profile['alpha']
|
||||
offset = (nb_stride * _y) + (_x // 2)
|
||||
if _x % 2 == 0:
|
||||
data[offset] |= (_c << 4)
|
||||
else:
|
||||
data[offset] |= _c
|
||||
|
||||
# update external information
|
||||
info['data'] = data
|
||||
info['data_size'] = data_size
|
||||
info['nb_stride'] = nb_stride
|
||||
info['data_size'] = data_size
|
||||
|
||||
def _image_convert(asset, profile_name):
|
||||
""" Image asset convertion
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - asset information
|
||||
> profile_name (str) - profile name information
|
||||
|
||||
@return
|
||||
> a dictionary with all image information
|
||||
"""
|
||||
# generate critical information and check posible error
|
||||
img_info = {
|
||||
'img' : Image.open(asset.path),
|
||||
'profile' : _profile_find(profile_name)
|
||||
}
|
||||
if not img_info['img']:
|
||||
log.error(f"unable to open the asset '{asset.path}', abord")
|
||||
return None
|
||||
if not img_info['profile']:
|
||||
log.error(f"unable to find the color profile '{profile_name}', abord")
|
||||
return None
|
||||
|
||||
# convert the bitmap and generate critical information
|
||||
_image_isolate_alpha(img_info)
|
||||
if img_info['profile']['is_indexed']:
|
||||
_image_encode_palette(img_info)
|
||||
_image_encode_bitmap(img_info)
|
||||
|
||||
# return generated information
|
||||
return img_info
|
||||
|
||||
## source file content generation
|
||||
|
||||
def _display_array(array, prefix='\t\t'):
|
||||
""" Display array information (only for p16* profile) """
|
||||
line = 0
|
||||
content = ''
|
||||
for pixels in array:
|
||||
if line == 0:
|
||||
content += prefix
|
||||
if line >= 1:
|
||||
content += ' '
|
||||
content += f'{pixels:#06x},'
|
||||
if (line := line + 1) >= 8:
|
||||
content += '\n'
|
||||
line = 0
|
||||
if line != 0:
|
||||
content += '\n'
|
||||
return content
|
||||
|
||||
def _image_generate_source_file(asset, info):
|
||||
"""Generate image source file
|
||||
|
||||
@args
|
||||
> asset (VxAsset) - asset information
|
||||
> info (dict) - hold image information
|
||||
|
||||
@return
|
||||
> file C content string
|
||||
"""
|
||||
img = info['img']
|
||||
profile = info['profile']
|
||||
|
||||
# generate basic header
|
||||
content = "#include <vhex/display/image/types.h>\n"
|
||||
content += "\n"
|
||||
content += f"/* {asset.name} - Vhex asset\n"
|
||||
content += " This object has been converted by using the vxSDK "
|
||||
content += "converter */\n"
|
||||
content += "const image_t " + f"{asset.name} = " + "{\n"
|
||||
content += f"\t.format = {profile['profile']},\n"
|
||||
content += "\t.flags = IMAGE_FLAGS_RO | IMAGE_FLAGS_OWN,\n"
|
||||
content += f"\t.color_count = {profile['palette_color_count']},\n"
|
||||
content += f"\t.width = {img.width},\n"
|
||||
content += f"\t.height = {img.height},\n"
|
||||
content += f"\t.stride = {info['nb_stride']},\n"
|
||||
|
||||
# encode bitmap table
|
||||
encode = 16 if profile['profile'] in ['p16', 'p16a'] else 8
|
||||
content += f"\t.data = (void*)(const uint{encode}_t [])" + "{\n"
|
||||
for _y in range(img.height):
|
||||
content += '\t\t'
|
||||
for _x in range(info['nb_stride']):
|
||||
pixel = info['data'][(_y * info['nb_stride']) + _x]
|
||||
if profile['profile'] in ['p16', 'p16a']:
|
||||
content += f'{pixel:#06x},'
|
||||
elif profile['profile'] in ['p8', 'p8a']:
|
||||
content += f'{pixel:#04x},'
|
||||
else:
|
||||
content += f'{pixel:3},'
|
||||
content += '\n'
|
||||
|
||||
content += '\t},\n'
|
||||
|
||||
# add palette information
|
||||
if 'palette_data' in info:
|
||||
content += "\t.palette = (void*)(const uint16_t []){\n"
|
||||
content += _display_array(info['palette_data'])
|
||||
content += "\t},\n"
|
||||
else:
|
||||
content += "\t.palette = NULL,\n"
|
||||
|
||||
# closure and return
|
||||
content += '};'
|
||||
return content
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def image_generate(asset, prefix_output, force_generate):
|
||||
""" Convert an image asset to a C source file
|
||||
|
||||
@args
|
||||
> asset (_VxAsset) - minimal asset information
|
||||
> prefix_output (str) - prefix for source file generation
|
||||
> force_generate (bool) - force generate the source file
|
||||
|
||||
@return
|
||||
> pathname of the generated file
|
||||
"""
|
||||
# check critical requirement
|
||||
if 'profile' not in asset.meta:
|
||||
log.error(f"[{asset.name}] missing profile information!")
|
||||
return ''
|
||||
|
||||
# check if the file already exists
|
||||
asset_src = f'{prefix_output}/{asset.name}_vximage.c'
|
||||
if not force_generate and os.path.exists(asset_src):
|
||||
return asset_src
|
||||
|
||||
#generate the source file content
|
||||
if not (img_info := _image_convert(asset, asset.meta['profile'])):
|
||||
return ''
|
||||
content = _image_generate_source_file(asset, img_info)
|
||||
|
||||
# generate the source file
|
||||
with open(asset_src, "w", encoding='utf8') as file:
|
||||
file.write(content)
|
||||
return asset_src
|
|
@ -0,0 +1,126 @@
|
|||
"""
|
||||
core.logger - Log wrapper
|
||||
"""
|
||||
import sys
|
||||
|
||||
__all__ = [
|
||||
'log'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
_LOG_DEBUG = 7
|
||||
_LOG_INFO = 6
|
||||
_LOG_NOTICE = 5
|
||||
_LOG_USER = 4
|
||||
_LOG_WARN = 3
|
||||
_LOG_ERR = 2
|
||||
_LOG_CRIT = 1
|
||||
_LOG_EMERG = 0
|
||||
|
||||
class _VxLogger():
|
||||
def __init__(self, logfile=None):
|
||||
self._logfile = logfile
|
||||
self._level = _LOG_USER
|
||||
self._indent = 0
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _print(self, level, text, skip_indent, fileno):
|
||||
if self._level < level:
|
||||
return 0
|
||||
if not skip_indent and self._level == _LOG_DEBUG and self._indent > 0:
|
||||
text = ('>>> ' * self._indent) + text
|
||||
print(text, file=fileno, end='', flush=True)
|
||||
return len(text) + 1
|
||||
|
||||
#---
|
||||
# Public properties
|
||||
#---
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
""" <property> handle print level """
|
||||
return self._level
|
||||
|
||||
@level.setter
|
||||
def level(self, level):
|
||||
""" <property> handle print level """
|
||||
if level < _LOG_EMERG or level > _LOG_DEBUG:
|
||||
print(f"[log] level update to {level} is not possible, ignored")
|
||||
return
|
||||
self._level = level
|
||||
|
||||
@property
|
||||
def indent(self):
|
||||
""" <property> handle indentation level for __LOG_DEBUG """
|
||||
return self._indent
|
||||
|
||||
@indent.setter
|
||||
def indent(self, indent):
|
||||
""" <property> handle indentation level for __LOG_DEBUG """
|
||||
if indent < 0:
|
||||
print(f"[log] indent update to {indent} is not possible, ignored")
|
||||
return
|
||||
self._indent = indent
|
||||
|
||||
#---
|
||||
# Public methods
|
||||
#---
|
||||
|
||||
def debug(self, text, end='\n', skip_indent=False):
|
||||
""" print debug log """
|
||||
return self._print(
|
||||
_LOG_DEBUG, f"[DEBUG] {text}{end}", skip_indent, sys.stdout
|
||||
)
|
||||
|
||||
def info(self, text, end='\n', skip_indent=False):
|
||||
""" print info log """
|
||||
return self._print(
|
||||
_LOG_INFO, f"[INFO] {text}{end}", skip_indent, sys.stdout
|
||||
)
|
||||
|
||||
def notice(self, text, end='\n', skip_indent=False):
|
||||
""" print notice log """
|
||||
return self._print(
|
||||
_LOG_NOTICE, f"[NOTICE] {text}{end}", skip_indent, sys.stdout
|
||||
)
|
||||
|
||||
def user(self, text, end='\n', skip_indent=False):
|
||||
""" print user log """
|
||||
return self._print(_LOG_USER, f"{text}{end}", skip_indent, sys.stdout)
|
||||
|
||||
def warn(self, text, end='\n', skip_indent=False):
|
||||
""" print warning log """
|
||||
return self._print(
|
||||
_LOG_WARN, f"[WARN] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
|
||||
def error(self, text, end='\n', skip_indent=False):
|
||||
""" print error log """
|
||||
return self._print(
|
||||
_LOG_ERR, f"[ERROR] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
|
||||
def critical(self, text, end='\n', skip_indent=False):
|
||||
""" print critical log """
|
||||
return self._print(
|
||||
_LOG_CRIT, f"[CRITICAL] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
|
||||
def emergency(self, text, end='\n', skip_indent=False):
|
||||
""" print emergency log """
|
||||
self._print(
|
||||
_LOG_EMERG, f"[EMERGENCY] {text}{end}", skip_indent, sys.stderr
|
||||
)
|
||||
sys.exit(84)
|
||||
|
||||
#---
|
||||
# Public functions
|
||||
#---
|
||||
|
||||
log = _VxLogger()
|
|
@ -0,0 +1,45 @@
|
|||
"""
|
||||
core.conv.pixel - Pixel converter utilities
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'pixel_rgb24to16'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def pixel_rgb24to16(rgb):
|
||||
""" convert RGB24 -> RGB16-565"""
|
||||
_r = (rgb[0] & 0xff) >> 3
|
||||
_g = (rgb[1] & 0xff) >> 2
|
||||
_b = (rgb[2] & 0xff) >> 3
|
||||
return (_r << 11) | (_g << 5) | _b
|
||||
|
||||
#def rgb1conv(pixel):
|
||||
# """ Convert RGB<BLACK> -> 1-bit color """
|
||||
# return pixel == (0, 0, 0)
|
||||
|
||||
#def rgb8conv(pixel):
|
||||
# """ Convert RGB8 -> """
|
||||
# return int((pixel[0] * 7) / 255) << 5 \
|
||||
# | int((pixel[1] * 4) / 255) << 3 \
|
||||
# | int((pixel[2] * 7) / 255) << 0
|
||||
|
||||
#def rgba8conv(pixel):
|
||||
# return int((pixel[0] * 4) / 256) << 6 \
|
||||
# | int((pixel[1] * 8) / 256) << 3 \
|
||||
# | int((pixel[2] * 4) / 256) << 1 \
|
||||
# | (len(pixel) >= 4 and pixel[3] == 0)
|
||||
|
||||
#def rgb16conv(pixel):
|
||||
# return int((pixel[0] * 31) / 255) << 11 \
|
||||
# | int((pixel[1] * 63) / 255) << 5 \
|
||||
# | int((pixel[2] * 31) / 255) << 0
|
||||
|
||||
#def rgba16conv(pixel):
|
||||
# return int((pixel[0] * 31) / 255) << 11 \
|
||||
# | int((pixel[1] * 63) / 255) << 6 \
|
||||
# | int((pixel[2] * 31) / 255) << 1 \
|
||||
# | (pixel[3] != 0)
|
|
@ -0,0 +1,54 @@
|
|||
"""
|
||||
cli.project - handles project creation and more
|
||||
"""
|
||||
import sys
|
||||
|
||||
from core.project import new_project
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'__VXSDK_MODULE_META__',
|
||||
'cli_validate',
|
||||
'cli_parse'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
__VXSDK_MODULE_META__ = (
|
||||
['p', 'project'],
|
||||
"project abstraction",
|
||||
r"""vxsdk-project
|
||||
Abstract project manipulation
|
||||
|
||||
USAGE:
|
||||
vxsdk project <COMMAND> [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print helps information
|
||||
|
||||
Common used commands:
|
||||
n, new Create a new project
|
||||
|
||||
See `vxsdk project <action> --help` for more information on a specific command
|
||||
"""
|
||||
)
|
||||
|
||||
def cli_validate(name):
|
||||
""" validate the module name """
|
||||
return name in __VXSDK_MODULE_META__[0]
|
||||
|
||||
def cli_parse(argv):
|
||||
""" Project subcommand entry """
|
||||
if len(argv) > 1:
|
||||
if '-h' in argv or '--help' in argv:
|
||||
log.user(__VXSDK_MODULE_META__[2])
|
||||
sys.exit(0)
|
||||
if len(argv) > 3:
|
||||
if argv[0] in ['n', 'new']:
|
||||
for path in argv[1:]:
|
||||
new_project(path)
|
||||
sys.exit(0)
|
||||
log.error(__VXSDK_MODULE_META__[2])
|
||||
sys.exit(84)
|
|
@ -0,0 +1,17 @@
|
|||
"""
|
||||
core.project - project abstraction
|
||||
"""
|
||||
|
||||
from core.project.new import new_project
|
||||
|
||||
__all__ = [
|
||||
'new'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def new(projpath):
|
||||
""" create a new project from default template """
|
||||
return new_project(projpath)
|
|
@ -0,0 +1,29 @@
|
|||
"""
|
||||
core.project.new - create a new project from default template
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from core.logger import log
|
||||
|
||||
__all__ = [
|
||||
'new_project'
|
||||
]
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
# (todo/CDE6) : change internal project name
|
||||
def new_project(project_path):
|
||||
""" create a new project """
|
||||
if os.path.exists(project_path):
|
||||
log.warn(f"The path {project_path} already exists !")
|
||||
return True
|
||||
origin_path = os.path.dirname(__file__)
|
||||
shutil.copytree(
|
||||
origin_path + '/../../assets/project/',
|
||||
project_path
|
||||
)
|
||||
log.user(f"project '{project_path}' successfully created !")
|
||||
return False
|
|
@ -0,0 +1,131 @@
|
|||
"""
|
||||
g3a_generator - Casio G1A file generator
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
from PIL import Image
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _u32(ptr, idx, data):
|
||||
""" unsigned 32bits wrapper """
|
||||
ptr[idx + 0] = (data & 0xff000000) >> 24
|
||||
ptr[idx + 1] = (data & 0x00ff0000) >> 16
|
||||
ptr[idx + 2] = (data & 0x0000ff00) >> 8
|
||||
ptr[idx + 3] = (data & 0x000000ff) >> 0
|
||||
|
||||
def _u16(ptr, idx, data):
|
||||
""" unsigned 16bits injection """
|
||||
ptr[idx + 0] = (data & 0xff00) >> 8
|
||||
ptr[idx + 1] = (data & 0x00ff) >> 0
|
||||
|
||||
def _u08(ptr, idx, data):
|
||||
""" unsigned 8bits injection """
|
||||
ptr[idx + 0] = (data & 0xff) >> 0
|
||||
|
||||
def _str(ptr, idx, data):
|
||||
""" string copy """
|
||||
for i, j in enumerate(data):
|
||||
_u08(ptr, idx + i, ord(j))
|
||||
|
||||
def _generate_standar_header(addin):
|
||||
""" generate Casio file standard header """
|
||||
_str(addin, 0x000, "USBPower") # watermark
|
||||
_u08(addin, 0x008, 0xf3) # addin file type
|
||||
_u08(addin, 0x009, 0x00) # watermark
|
||||
_u08(addin, 0x00a, 0x01) # watermark
|
||||
_u08(addin, 0x00b, 0x00) # watermark
|
||||
_u08(addin, 0x00c, 0x01) # watermark
|
||||
_u08(addin, 0x00d, 0x00) # watermark
|
||||
_u08(addin, 0x00e, 0x00) # LSB of file size + 0x41 (post)
|
||||
_u08(addin, 0x00f, 0x01) # watermark
|
||||
_u32(addin, 0x010, 0x00000000) # file size (MSB) (post)
|
||||
_u08(addin, 0x014, 0x00) # LSB of file size + 0xb8 (post)
|
||||
_u16(addin, 0x016, 0x0000) # sum of 8 words starting at 0x7100
|
||||
|
||||
def _generate_addin_subheader(addin):
|
||||
""" generate the g1a addin subheader """
|
||||
_str(addin, 0x020, "@VXGOS ") # checksum (post)
|
||||
_u08(addin, 0x02b, 0x00) # number of estrips
|
||||
_str(addin, 0x030, "01.00.0000") # addin version
|
||||
_str(addin, 0x03c, datetime.now().strftime("%Y.%m%d.%M%S")) # addin date
|
||||
|
||||
for i in range(0, 44):
|
||||
_u08(addin, 0x04c + i, 0x00) # icon
|
||||
|
||||
_str(addin, 0x1d4, "VXGOS ") # add name
|
||||
_u32(addin, 0x002e, 0x00000000) # filesize (post)
|
||||
|
||||
def _generate_addin_icon(addin, icon_path):
|
||||
""" encode mono icon in addin blob """
|
||||
with Image.open(icon_path) as icon:
|
||||
if icon.width != 30 and icon.height != 19:
|
||||
print(f"{icon_path}: image size should be 30x19 pixels, ignored")
|
||||
return
|
||||
pixels = icon.getdata()
|
||||
for _y in range(1, 18):
|
||||
for _x in range(0, 30):
|
||||
if pixels[(_y * 30) + _x] != (0, 0, 0, 255):
|
||||
continue
|
||||
_idx = 0x4c + ((_y * 4) + int((_x / 8)))
|
||||
_u08(addin, _idx, addin[_idx] | (0x80 >> (_x & 7)))
|
||||
|
||||
|
||||
def _generate_checksums(addin):
|
||||
""" generate all checksums requested for the g3a format """
|
||||
filesize = len(addin)
|
||||
|
||||
# standard header checksum
|
||||
_u08(addin, 0x010, (filesize & 0xff000000) >> 24)
|
||||
_u08(addin, 0x011, (filesize & 0x00ff0000) >> 16)
|
||||
_u08(addin, 0x012, (filesize & 0x0000ff00) >> 8)
|
||||
_u08(addin, 0x013, (filesize & 0x000000ff) >> 0)
|
||||
_u08(addin, 0x00e, addin[0x013] + 0x41)
|
||||
_u08(addin, 0x014, addin[0x013] + 0xb8)
|
||||
checksum = 0x00000
|
||||
for i in range(0, 8):
|
||||
checksum += int(
|
||||
addin[0x300 + (i * 2) : 0x302 + (i * 2)].hex(),
|
||||
base=16
|
||||
)
|
||||
_u16(addin, 0x016, checksum)
|
||||
|
||||
# addin-specific header checksum
|
||||
_u08(addin, 0x1f0, (filesize & 0xff000000) >> 24)
|
||||
_u08(addin, 0x1f1, (filesize & 0x00ff0000) >> 16)
|
||||
_u08(addin, 0x1f2, (filesize & 0x0000ff00) >> 8)
|
||||
_u08(addin, 0x1f3, (filesize & 0x000000ff) >> 0)
|
||||
|
||||
def _main(argv):
|
||||
""" main entry of the project """
|
||||
if len(argv) != 3:
|
||||
print('missing argument (./g1a_generator <output> <rawbin> <icon>)')
|
||||
sys.exit(84)
|
||||
|
||||
rawbin = b''
|
||||
with open(argv[1], 'rb') as rawfile:
|
||||
rawbin = rawfile.read()
|
||||
|
||||
addin = bytearray(0x200)
|
||||
_generate_standar_header(addin)
|
||||
_generate_addin_subheader(addin)
|
||||
_generate_addin_icon(addin, argv[2])
|
||||
addin += rawbin
|
||||
_generate_checksums(addin)
|
||||
|
||||
if os.path.exists(argv[0]):
|
||||
os.remove(argv[0])
|
||||
with open(argv[0], 'xb') as addinfile:
|
||||
addinfile.write(addin)
|
||||
return 0
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(_main(sys.argv[1:]))
|
|
@ -0,0 +1,138 @@
|
|||
"""
|
||||
generate - post-build script used to generate bootlaoder blob with ASLR
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
__all__ = [
|
||||
'generate_aslr_blob',
|
||||
'generate_image'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _patch_got_section(symfile, section):
|
||||
""" fetch all address of the GOT table
|
||||
"""
|
||||
for sec in section:
|
||||
if sec[0] != '.got':
|
||||
continue
|
||||
got_base_addr = int(sec[2], base=16)
|
||||
for i in range(0, int(sec[4], base=16), 4):
|
||||
print(f" > reloc GOT entry {got_base_addr+i:08x}...")
|
||||
symfile.write((got_base_addr + i).to_bytes(4, 'big'))
|
||||
break
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def generate_aslr_blob(binpath, symtab, sectab):
|
||||
""" generate bootloader final blob with ASLR
|
||||
|
||||
The objectif of this script is to generate the final bootloader blob with
|
||||
ASLR. To performs this, we will performs 3 steps:
|
||||
|
||||
* generate the raw binary file of the bootloader (objcpy)
|
||||
* generate the raw ALSR symbols table
|
||||
* generate the complet blootloader image
|
||||
|
||||
@args
|
||||
> binpath (str) - binary file
|
||||
> symtab (list) - list of all reloc information (readelf -r binpath)
|
||||
> sectab (list) - list of all sections information (readelf -S binpath)
|
||||
|
||||
@return
|
||||
> Nothings
|
||||
"""
|
||||
print('- generate raw binary...')
|
||||
if os.path.exists(f"{binpath}.raw"):
|
||||
os.remove(f"{binpath}.raw")
|
||||
cmd = f"sh-elf-vhex-objcopy -O binary -R .bss {binpath} {binpath}.raw"
|
||||
subprocess.run(
|
||||
cmd.split(),
|
||||
capture_output=False,
|
||||
check=False
|
||||
)
|
||||
|
||||
print('- generate raw symtab...')
|
||||
if os.path.exists(f"{binpath}.symtab"):
|
||||
os.remove(f"{binpath}.symtab")
|
||||
need_patch_got = False
|
||||
with open(f"{binpath}.symtab", 'xb') as symfile:
|
||||
for i, sym in enumerate(symtab):
|
||||
# direct 32bits address
|
||||
if sym[2] == 'R_SH_DIR32':
|
||||
print(f" > reloc 'R_SH_DIR32' sym at {sym[0]}")
|
||||
symfile.write(int(sym[0], base=16).to_bytes(4, 'big'))
|
||||
continue
|
||||
# GOT related jump
|
||||
if sym[2] == 'R_SH_GOT32':
|
||||
need_patch_got = True
|
||||
continue
|
||||
# Other
|
||||
if sym[2] not in [
|
||||
'R_SH_PLT32',
|
||||
'R_SH_GOT32',
|
||||
'R_SH_GOTPC',
|
||||
'R_SH_GOTOFF'
|
||||
]:
|
||||
print(f" > [{i}] reloc {sym[2]} not supported")
|
||||
sys.exit(84)
|
||||
if need_patch_got:
|
||||
_patch_got_section(symfile, sectab)
|
||||
symfile.write(int('00000000', base=16).to_bytes(4, 'big'))
|
||||
|
||||
print('- generate the full bootloader...')
|
||||
if os.path.exists(f"{binpath}.bzImage"):
|
||||
os.remove(f"{binpath}.bzImage")
|
||||
with open(f"{binpath}.bzImage", 'xb') as bzimgfile:
|
||||
with open(f"{binpath}.raw", 'rb') as rawbinfile:
|
||||
bzimgfile.write(rawbinfile.read())
|
||||
with open(f"{binpath}.symtab", 'rb') as symtabfile:
|
||||
bzimgfile.write(symtabfile.read())
|
||||
return f"{binpath}.bzImage"
|
||||
|
||||
def generate_image(prefix_build, bootloader_path, _):
|
||||
""" generate complet image (g3a) file
|
||||
|
||||
TODO
|
||||
"""
|
||||
|
||||
image = bytearray(0)
|
||||
with open(bootloader_path, 'rb') as bootloaderfile:
|
||||
image += bootloaderfile.read()
|
||||
# (todo) os/kernel
|
||||
image_size = len(image)
|
||||
|
||||
# patch first two instruction of the image (see <vxgos/bootloader>)
|
||||
image[0] = 0b00000000 # (MSB) nop
|
||||
image[1] = 0b00001001 # (LSB) nop
|
||||
image[2] = 0b11010000 # (MSB) mov.l @(1*, PC), r0
|
||||
image[3] = 0b00000001 # (LSB) mov.l @(1*, PC), r0
|
||||
image[8] = (image_size & 0xff000000) >> 24
|
||||
image[9] = (image_size & 0x00ff0000) >> 16
|
||||
image[10] = (image_size & 0x0000ff00) >> 8
|
||||
image[11] = (image_size & 0x000000ff) >> 0
|
||||
|
||||
bzimage = f"{prefix_build}/vxgos.bzImage"
|
||||
if os.path.exists(bzimage):
|
||||
os.remove(bzimage)
|
||||
with open(bzimage, 'xb') as bzimage:
|
||||
bzimage.write(image)
|
||||
prefix = os.path.dirname(__file__)
|
||||
subprocess.run(
|
||||
[
|
||||
'/usr/bin/env',
|
||||
'python3',
|
||||
f"{prefix}/g1a_generator.py",
|
||||
f"{prefix_build}/vxgos.g1a",
|
||||
f"{prefix_build}/vxgos.bzImage",
|
||||
f"{prefix}/icon.bmp",
|
||||
],
|
||||
capture_output=False,
|
||||
check=False
|
||||
)
|
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,151 @@
|
|||
"""
|
||||
g3a_generator - Casio G3A file generator
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
from PIL import Image
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _u32(ptr, idx, data):
|
||||
""" unsigned 32bits wrapper """
|
||||
ptr[idx + 0] = (data & 0xff000000) >> 24
|
||||
ptr[idx + 1] = (data & 0x00ff0000) >> 16
|
||||
ptr[idx + 2] = (data & 0x0000ff00) >> 8
|
||||
ptr[idx + 3] = (data & 0x000000ff) >> 0
|
||||
|
||||
def _u16(ptr, idx, data):
|
||||
""" unsigned 16bits injection """
|
||||
ptr[idx + 0] = (data & 0xff00) >> 8
|
||||
ptr[idx + 1] = (data & 0x00ff) >> 0
|
||||
|
||||
def _u08(ptr, idx, data):
|
||||
""" unsigned 8bits injection """
|
||||
ptr[idx + 0] = (data & 0xff) >> 0
|
||||
|
||||
def _str(ptr, idx, data):
|
||||
""" string copy """
|
||||
for i, j in enumerate(data):
|
||||
_u08(ptr, idx + i, ord(j))
|
||||
|
||||
def _pxl(ptr, idx, pixel):
|
||||
""" convert Image pixel tuple to RGB565 data """
|
||||
_r = (pixel[0] & 0xff) >> 3
|
||||
_g = (pixel[1] & 0xff) >> 2
|
||||
_b = (pixel[2] & 0xff) >> 3
|
||||
_c = (_r << 11) | (_g << 5) | _b
|
||||
ptr[idx + 0] = (_c & 0xff00) >> 8
|
||||
ptr[idx + 1] = (_c & 0x00ff) >> 0
|
||||
|
||||
def _generate_standar_header(addin):
|
||||
""" generate Casio file standard header """
|
||||
_str(addin, 0x0000, "USBPower") # watermark
|
||||
_u08(addin, 0x0008, 0x2c) # addin file type
|
||||
_u08(addin, 0x0009, 0x00) # watermark
|
||||
_u08(addin, 0x000a, 0x01) # watermark
|
||||
_u08(addin, 0x000b, 0x00) # watermark
|
||||
_u08(addin, 0x000c, 0x01) # watermark
|
||||
_u08(addin, 0x000d, 0x00) # watermark
|
||||
_u08(addin, 0x000e, 0x00) # LSB of file size + 0x41 (post)
|
||||
_u08(addin, 0x000f, 0x01) # watermark
|
||||
_u32(addin, 0x0010, 0x00000000) # file size (MSB) (post)
|
||||
_u08(addin, 0x0014, 0x00) # LSB of file size + 0xb8 (post)
|
||||
_u16(addin, 0x0016, 0x0000) # 8 words sum starting at 0x7100
|
||||
|
||||
def _generate_addin_subheader(addin):
|
||||
""" generate the g3a addin subheader """
|
||||
_u32(addin, 0x0020, 0x00000000) # checksum (post)
|
||||
_u16(addin, 0x0024, 0x0101) # watermark
|
||||
_u32(addin, 0x002e, 0x00000000) # filesize - 0x7000 - 4 (post)
|
||||
_str(addin, 0x0040, "VXOS") # title padded with zeros
|
||||
_u32(addin, 0x005c, 0x00000000) # file size (post)
|
||||
_str(addin, 0x0060, "@VXOS") # internal name zero padded
|
||||
for i in range(0, 8):
|
||||
_str(addin, 0x006b + (i * 24), "vxOS")
|
||||
_u08(addin, 0x012b, 0x00) # not usable by EAct
|
||||
_u32(addin, 0x012c, 0x00000000) # watermark
|
||||
_str(addin, 0x0130, "01.00.0000") # addin version
|
||||
_str(addin, 0x013c, datetime.now().strftime("%Y.%m%d.%M%S")) # addin date
|
||||
_str(addin, 0x0ebc, "VxOS.g3a") # filename
|
||||
|
||||
def _generate_checksums(addin):
|
||||
""" generate all checksums requested for the g3a format """
|
||||
filesize = len(addin)
|
||||
|
||||
# standard header checksum
|
||||
_u08(addin, 0x010, (filesize & 0xff000000) >> 24)
|
||||
_u08(addin, 0x011, (filesize & 0x00ff0000) >> 16)
|
||||
_u08(addin, 0x012, (filesize & 0x0000ff00) >> 8)
|
||||
_u08(addin, 0x013, (filesize & 0x000000ff) >> 0)
|
||||
_u08(addin, 0x00e, addin[0x013] + 0x41)
|
||||
_u08(addin, 0x014, addin[0x013] + 0xb8)
|
||||
checksum = 0x00000
|
||||
for i in range(0, 8):
|
||||
checksum += int(
|
||||
addin[0x7100 + (i * 2) : 0x7102 + (i * 2)].hex(),
|
||||
base=16
|
||||
)
|
||||
_u16(addin, 0x016, checksum)
|
||||
|
||||
# addin-specific header checksum
|
||||
_u32(addin, 0x02e, filesize - 0x7000 - 4)
|
||||
_u32(addin, 0x05c, filesize)
|
||||
checksum = 0x00000000
|
||||
for i in range(0x0000, 0x0020):
|
||||
checksum += addin[i]
|
||||
for i in range(0x0024, filesize - 4):
|
||||
checksum += addin[i]
|
||||
_u32(addin, 0x020, checksum)
|
||||
_u32(addin, -4, checksum)
|
||||
|
||||
def _generate_addin_icon(addin, offset, icon_path):
|
||||
""" generate the g3a header icon (selected or unselected) """
|
||||
with Image.open(icon_path) as icon:
|
||||
if icon.width != 92 and icon.height != 64:
|
||||
print(f"{icon_path}: image size should be 64x24 pixels, ignored")
|
||||
return
|
||||
pixels = icon.getdata()
|
||||
_idx = 0
|
||||
for _y in range(0, 92):
|
||||
for _x in range(0, 64):
|
||||
_pxl(addin, offset + (_idx * 2), pixels[_idx])
|
||||
_idx += 1
|
||||
|
||||
def _main(argv):
|
||||
""" main entry of the project """
|
||||
if len(argv) != 4:
|
||||
print(
|
||||
'missing argument '
|
||||
'(./g3a_generator <output> <rawbin> <icon-sel> <icon-unsel>)'
|
||||
)
|
||||
sys.exit(84)
|
||||
|
||||
rawbin = b''
|
||||
with open(argv[1], 'rb') as rawfile:
|
||||
rawbin = rawfile.read()
|
||||
|
||||
addin = bytearray(0x7000)
|
||||
_generate_standar_header(addin)
|
||||
_generate_addin_subheader(addin)
|
||||
_generate_addin_icon(addin, 0x1000, argv[3])
|
||||
_generate_addin_icon(addin, 0x4000, argv[2])
|
||||
addin += rawbin
|
||||
addin += bytearray(4)
|
||||
_generate_checksums(addin)
|
||||
|
||||
if os.path.exists(argv[0]):
|
||||
os.remove(argv[0])
|
||||
with open(argv[0], 'xb') as addinfile:
|
||||
addinfile.write(addin)
|
||||
return 0
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(_main(sys.argv[1:]))
|
|
@ -0,0 +1,139 @@
|
|||
"""
|
||||
generate - post-build script used to generate bootlaoder blob with ASLR
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
__all__ = [
|
||||
'generate_aslr_blob',
|
||||
'generate_image'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _patch_got_section(symfile, section):
|
||||
""" fetch all address of the GOT table
|
||||
"""
|
||||
for sec in section:
|
||||
if sec[0] != '.got':
|
||||
continue
|
||||
got_base_addr = int(sec[2], base=16)
|
||||
for i in range(0, int(sec[4], base=16), 4):
|
||||
print(f" > reloc GOT entry {got_base_addr+i:08x}...")
|
||||
symfile.write((got_base_addr + i).to_bytes(4, 'big'))
|
||||
break
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def generate_aslr_blob(binpath, symtab, sectab):
|
||||
""" generate bootloader final blob with ASLR
|
||||
|
||||
The objectif of this script is to generate the final bootloader blob with
|
||||
ASLR. To performs this, we will performs 3 steps:
|
||||
|
||||
* generate the raw binary file of the bootloader (objcpy)
|
||||
* generate the raw ALSR symbols table
|
||||
* generate the complet blootloader image
|
||||
|
||||
@args
|
||||
> binpath (str) - binary file
|
||||
> symtab (list) - list of all reloc information (readelf -r binpath)
|
||||
> sectab (list) - list of all sections information (readelf -S binpath)
|
||||
|
||||
@return
|
||||
> Nothings
|
||||
"""
|
||||
print('- generate raw binary...')
|
||||
if os.path.exists(f"{binpath}.raw"):
|
||||
os.remove(f"{binpath}.raw")
|
||||
cmd = f"sh-elf-vhex-objcopy -O binary -R .bss {binpath} {binpath}.raw"
|
||||
subprocess.run(
|
||||
cmd.split(),
|
||||
capture_output=False,
|
||||
check=False
|
||||
)
|
||||
|
||||
print('- generate raw symtab...')
|
||||
if os.path.exists(f"{binpath}.symtab"):
|
||||
os.remove(f"{binpath}.symtab")
|
||||
need_patch_got = False
|
||||
with open(f"{binpath}.symtab", 'xb') as symfile:
|
||||
for i, sym in enumerate(symtab):
|
||||
# direct 32bits address
|
||||
if sym[2] == 'R_SH_DIR32':
|
||||
print(f" > reloc 'R_SH_DIR32' sym at {sym[0]}")
|
||||
symfile.write(int(sym[0], base=16).to_bytes(4, 'big'))
|
||||
continue
|
||||
# GOT related jump
|
||||
if sym[2] == 'R_SH_GOT32':
|
||||
need_patch_got = True
|
||||
continue
|
||||
# Other
|
||||
if sym[2] not in [
|
||||
'R_SH_PLT32',
|
||||
'R_SH_GOT32',
|
||||
'R_SH_GOTPC',
|
||||
'R_SH_GOTOFF'
|
||||
]:
|
||||
print(f" > [{i}] reloc {sym[2]} not supported")
|
||||
sys.exit(84)
|
||||
if need_patch_got:
|
||||
_patch_got_section(symfile, sectab)
|
||||
symfile.write(int('00000000', base=16).to_bytes(4, 'big'))
|
||||
|
||||
print('- generate the full bootloader...')
|
||||
if os.path.exists(f"{binpath}.bzImage"):
|
||||
os.remove(f"{binpath}.bzImage")
|
||||
with open(f"{binpath}.bzImage", 'xb') as bzimgfile:
|
||||
with open(f"{binpath}.raw", 'rb') as rawbinfile:
|
||||
bzimgfile.write(rawbinfile.read())
|
||||
with open(f"{binpath}.symtab", 'rb') as symtabfile:
|
||||
bzimgfile.write(symtabfile.read())
|
||||
return f"{binpath}.bzImage"
|
||||
|
||||
def generate_image(prefix_build, bootloader_path, _):
|
||||
""" generate complet image (g3a) file
|
||||
|
||||
TODO
|
||||
"""
|
||||
|
||||
image = bytearray(0)
|
||||
with open(bootloader_path, 'rb') as bootloaderfile:
|
||||
image += bootloaderfile.read()
|
||||
# (todo) os/kernel
|
||||
image_size = len(image)
|
||||
|
||||
# patch first two instruction of the image (see <vxgos/bootloader>)
|
||||
image[0] = 0b00000000 # (MSB) nop
|
||||
image[1] = 0b00001001 # (LSB) nop
|
||||
image[2] = 0b11010000 # (MSB) mov.l @(1*, PC), r0
|
||||
image[3] = 0b00000001 # (LSB) mov.l @(1*, PC), r0
|
||||
image[8] = (image_size & 0xff000000) >> 24
|
||||
image[9] = (image_size & 0x00ff0000) >> 16
|
||||
image[10] = (image_size & 0x0000ff00) >> 8
|
||||
image[11] = (image_size & 0x000000ff) >> 0
|
||||
|
||||
bzimage = f"{prefix_build}/vxgos.bzImage"
|
||||
if os.path.exists(bzimage):
|
||||
os.remove(bzimage)
|
||||
with open(bzimage, 'xb') as bzimage:
|
||||
bzimage.write(image)
|
||||
prefix = os.path.dirname(__file__)
|
||||
subprocess.run(
|
||||
[
|
||||
'/usr/bin/env',
|
||||
'python3',
|
||||
f"{prefix}/g3a_generator.py",
|
||||
f"{prefix_build}/vxgos.g3a",
|
||||
f"{prefix_build}/vxgos.bzImage",
|
||||
f"{prefix}/icon-sel.png",
|
||||
f"{prefix}/icon-uns.png",
|
||||
],
|
||||
capture_output=False,
|
||||
check=False
|
||||
)
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,148 @@
|
|||
"""
|
||||
generate - post-build script used to generate bootlaoder blob with ASLR
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
__all__ = [
|
||||
'generate_aslr_blob',
|
||||
'generate_image'
|
||||
]
|
||||
|
||||
#---
|
||||
# Internals
|
||||
#---
|
||||
|
||||
def _patch_got_section(symfile, section):
|
||||
""" fetch all address of the GOT table
|
||||
"""
|
||||
for sec in section:
|
||||
if sec[0] != '.got':
|
||||
continue
|
||||
got_base_addr = int(sec[2], base=16)
|
||||
for i in range(0, int(sec[4], base=16), 8):
|
||||
print(f" > reloc GOT entry {got_base_addr+i:016x}...")
|
||||
symfile.write((got_base_addr + i).to_bytes(8, 'little'))
|
||||
break
|
||||
|
||||
#---
|
||||
# Public
|
||||
#---
|
||||
|
||||
def generate_aslr_blob(binpath, symtab, sectab):
|
||||
""" generate bootloader final blob with ASLR
|
||||
|
||||
The objectif of this script is to generate the final bootloader blob with
|
||||
ASLR. To performs this, we will performs 3 steps:
|
||||
|
||||
* generate the raw binary file of the bootloader (objcpy)
|
||||
* generate the raw ALSR symbols table
|
||||
* generate the complet blootloader image
|
||||
|
||||
@args
|
||||
> binpath (str) - binary file
|
||||
> symtab (list) - list of all reloc information (readelf -r binpath)
|
||||
> sectab (list) - list of all sections information (readelf -S binpath)
|
||||
|
||||
@return
|
||||
> Nothings
|
||||
"""
|
||||
print('- generate raw binary...')
|
||||
if os.path.exists(f"{binpath}.raw"):
|
||||
os.remove(f"{binpath}.raw")
|
||||
cmd = f"aarch64-linux-gnu-objcopy -O binary -R .bss {binpath} {binpath}.raw"
|
||||
subprocess.run(
|
||||
cmd.split(),
|
||||
capture_output=False,
|
||||
check=False
|
||||
)
|
||||
|
||||
print('- generate raw symtab...')
|
||||
if os.path.exists(f"{binpath}.symtab"):
|
||||
os.remove(f"{binpath}.symtab")
|
||||
need_patch_got = False
|
||||
with open(f"{binpath}.symtab", 'xb') as symfile:
|
||||
for i, sym in enumerate(symtab):
|
||||
# direct 64bits address
|
||||
if sym[2] == 'R_AARCH64_ABS64':
|
||||
print(f" > reloc 'R_AARCH64_ABS64' sym at {sym[0]}")
|
||||
symfile.write(int(sym[0], base=16).to_bytes(8, 'little'))
|
||||
continue
|
||||
|
||||
# need GOT patch
|
||||
if sym[2] == 'R_AARCH64_ADR_GOT':
|
||||
need_patch_got = True
|
||||
continue
|
||||
if sym[2] == 'R_AARCH64_LD64_GO':
|
||||
continue
|
||||
|
||||
# PC-related TAG
|
||||
if sym[2] in [
|
||||
'R_AARCH64_JUMP26',
|
||||
'R_AARCH64_CALL26',
|
||||
'R_AARCH64_ADR_PRE',
|
||||
'R_AARCH64_ADD_ABS',
|
||||
'R_AARCH64_LDST32_',
|
||||
]:
|
||||
continue
|
||||
|
||||
# unsupported TAG, aboard
|
||||
print(f" > [{i}] reloc {sym[2]} not supported")
|
||||
sys.exit(84)
|
||||
|
||||
# patch GOT if needed
|
||||
if need_patch_got:
|
||||
_patch_got_section(symfile, sectab)
|
||||
symfile.write(int('0000000000000000', base=16).to_bytes(8, 'little'))
|
||||
|
||||
print('- generate the full bootloader...')
|
||||
if os.path.exists(f"{binpath}.bzImage"):
|
||||
os.remove(f"{binpath}.bzImage")
|
||||
image_size = 0
|
||||
with open(f"{binpath}.bzImage", 'xb') as bzimgfile:
|
||||
with open(f"{binpath}.raw", 'rb') as rawbinfile:
|
||||
content = rawbinfile.read()
|
||||
bzimgfile.write(content)
|
||||
image_size += len(content)
|
||||
with open(f"{binpath}.symtab", 'rb') as symtabfile:
|
||||
content = symtabfile.read()
|
||||
bzimgfile.write(content)
|
||||
image_size += len(content)
|
||||
if image_size % 512:
|
||||
for _ in range(0, 512 - (image_size % 512)):
|
||||
bzimgfile.write(0x00.to_bytes(1))
|
||||
return f"{binpath}.bzImage"
|
||||
|
||||
|
||||
def generate_image(prefix_build, bootloader_path, _):
|
||||
""" generate complet image file
|
||||
"""
|
||||
# mov x0, #0x0000 = 0xd2800000 - 0xd29fffe0
|
||||
# mov x0, #0x0000, lsl 16 = 0xf2a00000 - 0xf2bfffe0
|
||||
# mov x0, #0x0000, lsl 32 = 0xf2c00000 - 0xf2dfffe0
|
||||
# mov x0, #0x0000, lsl 48 = 0xf2e00000 - 0xf2ffffe0
|
||||
|
||||
image = bytearray(0)
|
||||
with open(bootloader_path, 'rb') as bootloaderfile:
|
||||
image += bootloaderfile.read()
|
||||
# (todo) os/kernel
|
||||
image_size = len(image)
|
||||
|
||||
mov0 = 0xd2800000 | (((image_size & 0x000000000000ffff) >> 0) << 5)
|
||||
mov1 = 0xf2a00000 | (((image_size & 0x00000000ffff0000) >> 16) << 5)
|
||||
mov2 = 0xf2c00000 | (((image_size & 0x0000ffff00000000) >> 32) << 5)
|
||||
mov3 = 0xf2e00000 | (((image_size & 0xffff000000000000) >> 48) << 5)
|
||||
|
||||
image[0x40:0x44] = mov0.to_bytes(4, 'little')
|
||||
image[0x44:0x48] = mov1.to_bytes(4, 'little')
|
||||
image[0x48:0x4c] = mov2.to_bytes(4, 'little')
|
||||
image[0x4c:0x50] = mov3.to_bytes(4, 'little')
|
||||
|
||||
image[0x0c:0x14] = image_size.to_bytes(8, 'little')
|
||||
|
||||
bzimage = f"{prefix_build}/vxgos.bzImage"
|
||||
if os.path.exists(bzimage):
|
||||
os.remove(bzimage)
|
||||
with open(bzimage, 'xb') as bzimage:
|
||||
bzimage.write(image)
|
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1,16 @@
|
|||
[font8x12]
|
||||
path = 'font8x12.png'
|
||||
type = 'font'
|
||||
grid_size = '8x12'
|
||||
grid_padding = 1
|
||||
grid_border = 1
|
||||
proportional = false
|
||||
line_height = 9
|
||||
|
||||
[font3x5]
|
||||
path = 'font3x5.png'
|
||||
type = 'font'
|
||||
grid_size = '3x5'
|
||||
grid_padding = 1
|
||||
proportional = false
|
||||
line_height = 6
|
|
@ -0,0 +1,26 @@
|
|||
VXDEV_TOOLCHAIN_PREFIX = 'sh-elf-vhex-'
|
||||
VXDEV_TOOLCHAIN_PROCESSOR = 'sh'
|
||||
VXDEV_CFLAGS = [
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-D__SUPPORT_FX9860__',
|
||||
'-ffreestanding',
|
||||
'-nostdlib',
|
||||
'-fPIE',
|
||||
'-O1',
|
||||
'-mb',
|
||||
'-m4-nofpu',
|
||||
'-fstrict-volatile-bitfields',
|
||||
]
|
||||
VXDEV_LDFLAGS = [
|
||||
'-static',
|
||||
'-Wl,-Map=map',
|
||||
'-Wl,--build-id=none',
|
||||
'-Wl,--emit-relocs',
|
||||
]
|
||||
VXDEV_LIBS = [
|
||||
'-lgcc',
|
||||
]
|
||||
VXDEV_ASSETS = [
|
||||
'font3x5',
|
||||
]
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef FXCG50_BOOTLOADER_ASM_UTILS_H
|
||||
#define FXCG50_BOOTLOADER_ASM_UTILS_H 1
|
||||
|
||||
/* function() : define a function using special indication
|
||||
*
|
||||
* - the function's name must start with a '_'
|
||||
* - the function should start in a 4-aligned address in order to benefit from
|
||||
* the ILP (Instruction Level Parallelism) */
|
||||
#define function(name) \
|
||||
.balign 4 ;\
|
||||
.global _ ## name ;\
|
||||
_ ## name
|
||||
|
||||
/* FX9860_SYSCALL_TRAMPOLINE - Casio's syscall trampoline address */
|
||||
#define FX9860_SYSCALL_TRAMPOLINE 0x80010070
|
||||
|
||||
/* syscall() : helper used to quickly define Casio syscall invokation
|
||||
*
|
||||
* - the syscall trampoline code is common for all syscall
|
||||
* - r0 contain the syscall ID */
|
||||
#define syscall(id) \
|
||||
mov.l 1f, r2 ;\
|
||||
mov.l 2f, r0 ;\
|
||||
jmp @r2 ;\
|
||||
nop ;\
|
||||
.balign 4 ;\
|
||||
1: .long FX9860_SYSCALL_TRAMPOLINE ;\
|
||||
2: .long id
|
||||
|
||||
#endif /* FXCG50_BOOTLOADER_ASM_UTILS_H */
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef FXCG50_KEYCODE_H
|
||||
#define FXCG50_KEYCODE_H
|
||||
|
||||
/* KEYSCODE_GEN() : generate keycode */
|
||||
#define KEYCODE_GEN(row, column) \
|
||||
(((row & 0x0f) << 4) | ((column & 0x0f) << 0))
|
||||
|
||||
/* Define all keycode */
|
||||
typedef enum
|
||||
{
|
||||
KEY_F1 = 0x41,
|
||||
KEY_F2 = 0x42,
|
||||
KEY_F3 = 0x43,
|
||||
KEY_F4 = 0x44,
|
||||
KEY_F5 = 0x45,
|
||||
KEY_F6 = 0x46,
|
||||
|
||||
KEY_SHIFT = 0x49,
|
||||
KEY_OPTN = 0x4a,
|
||||
KEY_VARS = 0x4b,
|
||||
KEY_MENU = 0x4c,
|
||||
KEY_LEFT = 0x4d,
|
||||
KEY_UP = 0x4e,
|
||||
|
||||
KEY_ALPHA = 0x31,
|
||||
KEY_SQUARE = 0x32,
|
||||
KEY_POWER = 0x33,
|
||||
KEY_EXIT = 0x34,
|
||||
KEY_DOWN = 0x35,
|
||||
KEY_RIGHT = 0x36,
|
||||
|
||||
KEY_XOT = 0x39,
|
||||
KEY_LOG = 0x3a,
|
||||
KEY_LN = 0x3b,
|
||||
KEY_SIN = 0x3c,
|
||||
KEY_COS = 0x3d,
|
||||
KEY_TAN = 0x3e,
|
||||
|
||||
KEY_FRAC = 0x21,
|
||||
KEY_FD = 0x22,
|
||||
KEY_LEFTP = 0x23,
|
||||
KEY_RIGHTP = 0x24,
|
||||
KEY_COMMA = 0x25,
|
||||
KEY_ARROW = 0x26,
|
||||
|
||||
KEY_7 = 0x29,
|
||||
KEY_8 = 0x2a,
|
||||
KEY_9 = 0x2b,
|
||||
KEY_DEL = 0x2c,
|
||||
|
||||
KEY_4 = 0x11,
|
||||
KEY_5 = 0x12,
|
||||
KEY_6 = 0x13,
|
||||
KEY_MUL = 0x14,
|
||||
KEY_DIV = 0x15,
|
||||
|
||||
KEY_1 = 0x19,
|
||||
KEY_2 = 0x1a,
|
||||
KEY_3 = 0x1b,
|
||||
KEY_PLUS = 0x1c,
|
||||
KEY_MINUS = 0x1d,
|
||||
|
||||
KEY_0 = 0x01,
|
||||
KEY_DOT = 0x02,
|
||||
KEY_EXP = 0x03,
|
||||
KEY_NEG = 0x04,
|
||||
KEY_EXE = 0x05,
|
||||
|
||||
KEY_UNUSED = 0xff,
|
||||
KEY_NONE = 0xfe,
|
||||
} KEY_t;
|
||||
|
||||
#endif /* FXCG50_KEYCODE_H */
|
|
@ -0,0 +1,17 @@
|
|||
#include "bootloader/display.h"
|
||||
#include "bootloader/bios.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* _bios_dfont_get() : get font information */
|
||||
int _bios_dfont_get(struct font **font)
|
||||
{
|
||||
extern struct font font3x5;
|
||||
|
||||
if (font == NULL)
|
||||
return -1;
|
||||
*font = &font3x5;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include "bootloader/bios.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* _bios_dinfo() : get display information */
|
||||
void _bios_dinfo(size_t *width, size_t *height)
|
||||
{
|
||||
if (width != NULL)
|
||||
*width = 128;
|
||||
if (height != NULL)
|
||||
*height = 64;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootloader/bios.h"
|
||||
#include "bootloader/display.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* _bios_dpixel() : dpixel wrapper */
|
||||
void _bios_dpixel(int x, int y, int color)
|
||||
{
|
||||
extern uint8_t vram[];
|
||||
int mask;
|
||||
int indx;
|
||||
|
||||
if ((unsigned)x >= 128 || (unsigned)y >= 64)
|
||||
return;
|
||||
|
||||
indx = ((y * 128) + x) / 8;
|
||||
mask = 0x80 >> (x & 7);
|
||||
|
||||
switch (color) {
|
||||
case C_BLACK:
|
||||
vram[indx] |= mask;
|
||||
break;
|
||||
case C_WHITE:
|
||||
vram[indx] &= ~mask;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
#include <stdint.h>
|
||||
|
||||
//---
|
||||
// Internals
|
||||
//---
|
||||
|
||||
// Device specification sheet
|
||||
|
||||
/* This version number is 1 for the old T6K11 (T6K73A (2005-11-30)) everyone
|
||||
* knows, and 2 for the newer one found in the Graph 35+E II (ML9801A
|
||||
* (2018-07-05)).
|
||||
* Documentation is available only for the T6K11 (version 1 is close).
|
||||
* Dumps of Bdisp_PutDisp_DD() are used to drive version 2. */
|
||||
static int t6k11_version = -1;
|
||||
|
||||
/* Screen registers on the original T6K11. Registers 8..11 and 13..15 are test
|
||||
registers and must not be used! */
|
||||
enum {
|
||||
reg_display = 0,
|
||||
reg_counter = 1,
|
||||
reg_analog = 2,
|
||||
reg_alternate = 3,
|
||||
|
||||
reg_yaddr = 4, /* These two use the same register */
|
||||
reg_xaddr = 4, /* (interpretation depends on count mode) */
|
||||
|
||||
reg_zaddr = 5,
|
||||
reg_contrast = 6,
|
||||
reg_data = 7,
|
||||
reg_daconv = 12,
|
||||
};
|
||||
|
||||
/* Automatic counter increment during read/write */
|
||||
enum {
|
||||
cnt_ydown = 0,
|
||||
cnt_yup = 1,
|
||||
cnt_xdown = 2,
|
||||
cnt_xup = 3,
|
||||
};
|
||||
|
||||
// Device communication primitives
|
||||
|
||||
/* I/O may be performed either with RS = 0 or RS = 1.
|
||||
* The memory-mapping of the device I/O maps bit 16 of the address to pin RS.
|
||||
* There may be other mapped pins in the address. (?) */
|
||||
|
||||
/* RS = 0: Register selection */
|
||||
static volatile uint8_t *sel = (void *)0xb4000000;
|
||||
/* RS = 1: Command data or vram data */
|
||||
static volatile uint8_t *cmd = (void *)0xb4010000;
|
||||
|
||||
/* command() - send a command to set the value of a register
|
||||
@reg Register number
|
||||
@data Value to set in reg */
|
||||
static void command(uint8_t reg, uint8_t data)
|
||||
{
|
||||
*sel = reg;
|
||||
*cmd = data;
|
||||
}
|
||||
|
||||
/* write_row() - send 16 bytes to the display driver
|
||||
@buf Buffer to take data from */
|
||||
static void write_row(const uint8_t *buf)
|
||||
{
|
||||
/* Unroll the loop for more speed */
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
*cmd = *buf++;
|
||||
}
|
||||
|
||||
// Driver functions
|
||||
|
||||
/* t6k11_display() - send vram data to the LCD device */
|
||||
static void t6k11_display_t6k73(const void *vram)
|
||||
{
|
||||
for(int y = 0; y < 64; y++)
|
||||
{
|
||||
/* Set the X-address register for this row */
|
||||
command(reg_xaddr, y | 0xc0);
|
||||
/* Use Y-Up mode */
|
||||
command(reg_counter, cnt_yup);
|
||||
/* Start counting Y from 0 */
|
||||
command(reg_yaddr, 0);
|
||||
|
||||
/* Send the row's data to the device */
|
||||
*sel = reg_data;
|
||||
write_row(vram);
|
||||
vram += 128 / 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* t6k11_display() - send vram data to the LCD device */
|
||||
static void t6k11_display_ml9801(const void *vram)
|
||||
{
|
||||
for(int y = 0; y < 64; y++)
|
||||
{
|
||||
command(8, y | 0x80);
|
||||
command(8, 4);
|
||||
|
||||
*sel = 10;
|
||||
write_row(vram);
|
||||
vram += 128 / 8;
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* expose VRAM here */
|
||||
uint8_t vram[(128 * 64) / 8];
|
||||
|
||||
/* _bios_dupdate() : small T6K73A and ML9801A driver */
|
||||
void _bios_dupdate(void)
|
||||
{
|
||||
extern uint8_t vram[];
|
||||
extern int t6k11_version;
|
||||
|
||||
/* Tell Graph 35+E II from OS version (this is accurate unless someone
|
||||
* tweaks an OS file enough to de-correlate the version of the OS and
|
||||
* the version of the display and storage memory drivers, which, let's
|
||||
* be real, is enough for now. */
|
||||
if (t6k11_version == -1) {
|
||||
char *version = (void *)0x80010020;
|
||||
t6k11_version = (version[1] == '3') ? 2 : 1;
|
||||
}
|
||||
|
||||
/* branch to the real driver */
|
||||
if(t6k11_version == 1) t6k11_display_t6k73(vram);
|
||||
if(t6k11_version == 2) t6k11_display_ml9801(vram);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include "fx9860/asm_utils.h"
|
||||
|
||||
.text
|
||||
|
||||
!---
|
||||
! Public
|
||||
!---
|
||||
|
||||
/* _bios_free() (free) : free allocated memory */
|
||||
function(_bios_free):
|
||||
syscall(0xacc)
|
|
@ -0,0 +1,11 @@
|
|||
#include "fx9860/asm_utils.h"
|
||||
|
||||
.text
|
||||
|
||||
!---
|
||||
! Public
|
||||
!---
|
||||
|
||||
/* _bios_malloc() (malloc) : allocate memory */
|
||||
function(_bios_malloc):
|
||||
syscall(0xacd)
|
|
@ -0,0 +1,89 @@
|
|||
#include "bootloader/console.h"
|
||||
#include "fx9860/keycode.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* console_key_handle_special() : handle special key handling */
|
||||
// TODO
|
||||
// - F_UP -> history
|
||||
// - F_DOWN -> history
|
||||
int console_key_handle_special(struct console *console, int key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KEY_SHIFT:
|
||||
console->input.mode.shift ^= 1;
|
||||
return 1;
|
||||
case KEY_ALPHA:
|
||||
console->input.mode.maj ^= 1;
|
||||
return 1;
|
||||
case KEY_OPTN:
|
||||
console->input.mode.ctrl ^= 1;
|
||||
return 1;
|
||||
case KEY_DOT:
|
||||
console_buffer_in_insert(console, ' ');
|
||||
return 1;
|
||||
case KEY_DEL:
|
||||
console_buffer_in_remove(console);
|
||||
return 1;
|
||||
case KEY_EXE:
|
||||
console->input.mode.exit = 1;
|
||||
return 1;
|
||||
case KEY_LEFT:
|
||||
console_buffer_in_cursor_move(console, -1);
|
||||
return 1;
|
||||
case KEY_RIGHT:
|
||||
console_buffer_in_cursor_move(console, 1);
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* console_key_handle(): Update the internal buffer with the given key code */
|
||||
int console_key_handle(struct console *console, int key)
|
||||
{
|
||||
static const uint8_t keylist_alpha[] = {
|
||||
KEY_XOT, KEY_LOG, KEY_LN, KEY_SIN, KEY_COS, KEY_TAN,
|
||||
KEY_FRAC, KEY_FD, KEY_LEFTP, KEY_RIGHTP, KEY_COMMA, KEY_ARROW,
|
||||
KEY_7, KEY_8, KEY_9,
|
||||
KEY_4, KEY_5, KEY_6, KEY_MUL, KEY_DIV,
|
||||
KEY_1, KEY_2, KEY_3, KEY_PLUS, KEY_MINUS,
|
||||
KEY_0, 0xff
|
||||
};
|
||||
static const uint8_t keylist_num[] = {
|
||||
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4,
|
||||
KEY_5, KEY_6, KEY_7, KEY_8, KEY_9,
|
||||
KEY_PLUS, KEY_MINUS, KEY_MUL, KEY_DIV,
|
||||
KEY_LEFTP, KEY_RIGHTP, KEY_COMMA, KEY_POWER,
|
||||
KEY_DOT, KEY_FD, KEY_ARROW, 0xff
|
||||
};
|
||||
static const char keylist_num_char[] = "0123456789+-x/(),^.|_";
|
||||
const uint8_t *keycode_list;
|
||||
char character;
|
||||
int i;
|
||||
|
||||
/* Get the appropriate key list */
|
||||
keycode_list = keylist_alpha;
|
||||
if (console->input.mode.shift == 1)
|
||||
keycode_list = keylist_num;
|
||||
|
||||
/* Try to find the pressed key. */
|
||||
i = -1;
|
||||
while (keycode_list[++i] != 0xff && keycode_list[i] != key);
|
||||
if (keycode_list[i] != key)
|
||||
return 0;
|
||||
|
||||
/* handle mode then update the buffer */
|
||||
if (console->input.mode.shift == 0) {
|
||||
character = 'a' + i;
|
||||
if (console->input.mode.maj == 1)
|
||||
character = 'A' + i;
|
||||
} else {
|
||||
character = keylist_num_char[i];
|
||||
}
|
||||
console_buffer_in_insert(console, character);
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#include "bootloader/console.h"
|
||||
#include "fx9860/keycode.h"
|
||||
|
||||
//---
|
||||
// Internlas
|
||||
//---
|
||||
|
||||
/* keysc_fetch() : scan the KEYSC hardware module */
|
||||
static int keysc_fetch(void)
|
||||
{
|
||||
uint16_t volatile *SH7305_KEYSC = (void*)0xa44b0000;
|
||||
int column;
|
||||
int key;
|
||||
int row;
|
||||
int registered;
|
||||
uint16_t udata;
|
||||
|
||||
/* update the keycache */
|
||||
row = 6;
|
||||
key = 0x5f;
|
||||
registered = 0x0000;
|
||||
while (--row >= 0)
|
||||
{
|
||||
column = 16;
|
||||
udata = SH7305_KEYSC[row];
|
||||
if (registered != 0x0000)
|
||||
continue;
|
||||
while (--column >= 0)
|
||||
{
|
||||
if ((udata & (0x8000 >> column)) != 0) {
|
||||
registered = KEYCODE_GEN(row, column);
|
||||
break;
|
||||
}
|
||||
key -= 1;
|
||||
}
|
||||
}
|
||||
return registered;
|
||||
}
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* console_key_get() : small one-shot key waiter */
|
||||
int console_key_get(void)
|
||||
{
|
||||
static uint16_t prev_key = 0xffff;
|
||||
|
||||
if (prev_key != 0xffff) {
|
||||
while (1) {
|
||||
if (prev_key != keysc_fetch())
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (1) {
|
||||
prev_key = keysc_fetch();
|
||||
if (prev_key != 0x0000)
|
||||
break;
|
||||
}
|
||||
return prev_key;
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
#include "fx9860/asm_utils.h"
|
||||
|
||||
.section .bootloader.pre_text, "ax"
|
||||
|
||||
/* ___fxcg50_initialize() : bootstrap routine
|
||||
|
||||
We are currently virtualized as a common program (addin) at 0x00300000, but the
|
||||
code currently executed is physically fragmented through the ROM. So, we cannot
|
||||
perform ASLR self-relocation here.
|
||||
|
||||
The first thing we need to do is find the "user" RAM allocated by Casio (by
|
||||
analyzing the TLB) and then performs a "self-translation" into this place.
|
||||
After that, we will be able to perform ASLR self-relocation and classic
|
||||
"kernel" bootstrapping.. */
|
||||
|
||||
function(__fxcg50_primary_bootloader):
|
||||
|
||||
! ---
|
||||
! *CRITICAL*
|
||||
!
|
||||
! The next two instructions will be patched *DURING COMPILE TIME* with the
|
||||
! complete image size (bootloader, ASLR, kernel, OS,...) as follows:
|
||||
! > mov.l complet_image_size, r0
|
||||
! > nop
|
||||
! If not patched, the code will just return and indicate to Casio not to
|
||||
! restart the application.
|
||||
! ---
|
||||
|
||||
rts
|
||||
mov #1, r0
|
||||
bra translation_entry
|
||||
nop
|
||||
.long 0x00000000
|
||||
|
||||
translation_entry:
|
||||
|
||||
! ---
|
||||
! prepare alias
|
||||
!
|
||||
! We don't need to setup the stack because we are loaded from CasioABS
|
||||
! (Casio OS) as a simple program (addin) with a given stack, which we will
|
||||
! not use even if we use "special" registers such as r8, r9, ... as we will
|
||||
! be careful to never return from here.
|
||||
!
|
||||
! @note
|
||||
! - r0 is reused
|
||||
! - r1 is reused
|
||||
! - r2 is reused
|
||||
! - r7 is reused
|
||||
! - r14 is reused
|
||||
! ---
|
||||
|
||||
#define counter r0
|
||||
#define utlb_addr_val r1
|
||||
#define utlb_data_val r2
|
||||
#define uram_phys_addr r3
|
||||
#define rom_base_addr r4
|
||||
#define rom_image_size r5
|
||||
#define utlb_data_ptr r6
|
||||
#define utlb_addr_ptr r7
|
||||
#define uram_virt_ptr r8
|
||||
#define uram_phys_size r9
|
||||
#define utlb_valid_bit r10
|
||||
#define utlb_data_ptr_saved r11
|
||||
#define utlb_magic_array r12
|
||||
#define utlb_addr_ptr_saved r13
|
||||
#define utlb_data_ppn_mask r14
|
||||
|
||||
! ---
|
||||
! Save critical information
|
||||
!
|
||||
! Calculate the runtime loaded address of the addin and save the
|
||||
! compile-time image size information
|
||||
! ---
|
||||
|
||||
! save image size
|
||||
mov r0, rom_image_size
|
||||
|
||||
! precalculate runtime loaded address
|
||||
mova utlb_fetch_uram_info, r0
|
||||
add #(___fxcg50_primary_bootloader - utlb_fetch_uram_info), r0
|
||||
mov r0, rom_base_addr
|
||||
|
||||
! ---
|
||||
! Configure cache
|
||||
!
|
||||
! This is a critical part because we will move to the URAM and perform
|
||||
! self-patching symbols during our execution. So, we need to have a
|
||||
! consistent cache behavior to help the MPU follow our instructions.
|
||||
!
|
||||
! The SH7305 has a special register for that called Cache Control Register
|
||||
! (CCR), and it seems tobe the same as SH7724 (except that no official POC
|
||||
! or information can invalidate the instruction Cache (IC) without causing
|
||||
! the machine to crash):
|
||||
!
|
||||
! - Indicate that P1 area use the "Write-back" method
|
||||
! - Indicate that P0/P3 areas use the "Write-through" method
|
||||
! - Enable Operand Cache (OC)
|
||||
! - Enable Instruction Cache (IC)
|
||||
! - Invalidate all Operand Cache (OC) entries
|
||||
! ---
|
||||
|
||||
mov.l ccr_register_addr, r0
|
||||
mov.l ccr_register_data, r1
|
||||
mov.l r1, @r0
|
||||
synco
|
||||
|
||||
! ---
|
||||
! UTLB scan to find URAM information
|
||||
!
|
||||
! As explained above, we first need to find the user RAM given by Casio for
|
||||
! our application, as we know that is an unused and stable space for us.
|
||||
!
|
||||
! We will scan each TLB entry to find the user's RAM. The memory is
|
||||
! virtually mapped by Casio using the same historical virtual address:
|
||||
! 0x08100000. Also, all the given RAM is entierly (?) mapped by the
|
||||
! operating system. Thus, we walk through the TLB until we don't find the
|
||||
! next memory fragment; this will allow us to find the size of the RAM
|
||||
! (which varies between models and emulators).
|
||||
!
|
||||
! We will also take advantage of the fact that Casio *MAP* the virtual
|
||||
! address 0x00000000 (NULL) for no valid reason. So, if we find this
|
||||
! mapping, we will invalidate it to be sure that a NULL manipulated pointer
|
||||
! will cause a TLBmiss exception.
|
||||
!
|
||||
! TODO : invalidate NULL page
|
||||
! ---
|
||||
|
||||
utlb_fetch_uram_info:
|
||||
|
||||
! fetch external information
|
||||
mov.l data_00000100, utlb_valid_bit
|
||||
mov.l data_08100000, uram_virt_ptr
|
||||
mov.l data_f6000000, utlb_addr_ptr
|
||||
mov.l data_14100c0a, utlb_magic_array
|
||||
mov.l data_1ffffc00, utlb_data_ppn_mask
|
||||
mov.l data_f7000000, utlb_data_ptr
|
||||
|
||||
! prepare internal vars
|
||||
mov #0, counter
|
||||
mov #-1, uram_phys_addr
|
||||
mov #0, uram_phys_size
|
||||
mov utlb_data_ptr, utlb_data_ptr_saved
|
||||
mov utlb_addr_ptr, utlb_addr_ptr_saved
|
||||
|
||||
utlb_walk_loop:
|
||||
! validity check
|
||||
! @note
|
||||
! - check the validity bit for each UTLB data and address entry
|
||||
! - both data and address entry have the same Valid bit position
|
||||
mov.l @utlb_addr_ptr, utlb_addr_val
|
||||
tst utlb_valid_bit, utlb_addr_val
|
||||
bt utlb_walk_cond_check
|
||||
mov.l @utlb_data_ptr, utlb_data_val
|
||||
tst utlb_valid_bit, utlb_data_val
|
||||
bt.s utlb_walk_cond_check
|
||||
|
||||
! check VPN validity
|
||||
! @note
|
||||
! - "UTLB Address Array" (p239) - Figure 7.14
|
||||
! - we need to clear the first 10 bits of the fetched UTLB data to get the
|
||||
! the "real" virtual address (eliminate ASID, Dirty and Valid bits)
|
||||
shlr8 utlb_addr_val
|
||||
shlr2 utlb_addr_val
|
||||
shll8 utlb_addr_val
|
||||
shll2 utlb_addr_val
|
||||
cmp/eq uram_virt_ptr, utlb_addr_val
|
||||
bf.s utlb_walk_cond_check
|
||||
|
||||
! fetch the page size
|
||||
! @note
|
||||
! - "Unified TLB (UTLB) Configuration"(p221)
|
||||
! - page size is weird to calculate for many hardware reasons, and this code
|
||||
! is the literal translation of :
|
||||
! > size = ((data->SZ1 << 1) | data->SZ2) << 3;
|
||||
! > size = 1 << ((0x14100c0a >> size) & 0xff);
|
||||
mov #-1, r1
|
||||
mov utlb_data_val, r0
|
||||
tst #128, r0
|
||||
mov #-1, r7
|
||||
negc r1, r1
|
||||
tst #16, r0
|
||||
add r1, r1
|
||||
negc r7, r7
|
||||
or r7, r1
|
||||
mov utlb_magic_array,r7
|
||||
shll2 r1
|
||||
add r1, r1
|
||||
neg r1, r1
|
||||
shad r1, r7
|
||||
extu.b r7, r1
|
||||
mov #1, r7
|
||||
shld r1, r7
|
||||
|
||||
! update counter / information
|
||||
add r7, uram_phys_size
|
||||
add r7, uram_virt_ptr
|
||||
|
||||
! check if the URAM physical address is already set
|
||||
mov uram_phys_addr,r0
|
||||
cmp/eq #-1,r0
|
||||
bf utlb_page_found_restart
|
||||
|
||||
! calculate the physical address of the page (URAM)
|
||||
! @note
|
||||
! - "UTLB Data Array"(p240) - Figure 7.15
|
||||
! - to fetch the Physical Address, we just need to isolate the PPN
|
||||
and utlb_data_ppn_mask, utlb_data_val
|
||||
shlr8 utlb_data_val
|
||||
shlr2 utlb_data_val
|
||||
mov utlb_data_val, uram_phys_addr
|
||||
shll8 uram_phys_addr
|
||||
shll2 uram_phys_addr
|
||||
|
||||
utlb_page_found_restart:
|
||||
mov r13, utlb_addr_ptr
|
||||
mov r11, utlb_data_ptr
|
||||
bra utlb_walk_loop
|
||||
mov #0, counter
|
||||
|
||||
utlb_walk_cond_check:
|
||||
! update internal counter
|
||||
! @notes
|
||||
! - only 64 UTLB entry
|
||||
! - UTLB entry (for data and address) gap is 0x100
|
||||
mov.l data_00000100, r1
|
||||
add #1, counter
|
||||
cmp/eq #64, counter
|
||||
add r1, utlb_addr_ptr
|
||||
bf.s utlb_walk_loop
|
||||
add r1, utlb_data_ptr
|
||||
|
||||
! ---
|
||||
! Self-translation to URAM
|
||||
!
|
||||
! Now that we have the user RAM entry address (uram_phys_addr) and its size
|
||||
! (uram_phys_size), we can self-translate to this location using a dummy
|
||||
! byte-per-byte copy.
|
||||
!
|
||||
! Note that, for now, no random installation offset is performed since
|
||||
! predicting uncertain behavior is complex to choose for now.
|
||||
! ---
|
||||
|
||||
self_translation:
|
||||
! fetch bootloader ROM geometry information
|
||||
mov rom_base_addr, r0
|
||||
mov rom_image_size, r2
|
||||
|
||||
! generate uncachable P2 URAM address
|
||||
! TODO
|
||||
! - random offset
|
||||
! - check oversize
|
||||
mov.l data_a0000000, r1
|
||||
or uram_phys_addr, r1
|
||||
|
||||
! dump the complet image into URAM
|
||||
self_dump_uram:
|
||||
mov.b @r0, r14
|
||||
mov.b r14, @r1
|
||||
dt r2
|
||||
add #1, r0
|
||||
add #1, r1
|
||||
bf.s self_dump_uram
|
||||
nop
|
||||
|
||||
! Prepare the self-translation by calculating the new PC position using a
|
||||
! P1 address to allow caching to be performed.
|
||||
! @note
|
||||
! - ___fxcg50_bootloader_start is a global symbol compiled with NULL as the
|
||||
! base address. So, we don't have to calculate the gap between the start
|
||||
! of the ROM and the symbol.
|
||||
mov.l data_80000000, r1
|
||||
or uram_phys_addr, r1
|
||||
mov.l real_bootloader_start, r0
|
||||
add r1, r0
|
||||
|
||||
! self-translation
|
||||
mov rom_image_size, r6
|
||||
mov uram_phys_addr, r5
|
||||
mov rom_base_addr, r4
|
||||
jmp @r0
|
||||
nop
|
||||
|
||||
.balign 4
|
||||
|
||||
data_08100000: .long 0x08100000 ! Casio addin load virtual address
|
||||
data_f6000000: .long 0xf6000000 ! SH7305 UTLB Address address
|
||||
data_f7000000: .long 0xf7000000 ! SH7305 UTLB Data addresss
|
||||
data_14100c0a: .long 0x14100c0a ! Magic UTLB page size table
|
||||
data_1ffffc00: .long 0x1ffffc00 ! UTLB Address PPN mask
|
||||
data_00000100: .long 0x00000100 ! UTLB entry gap and UTLB validity bit
|
||||
data_a0000000: .long 0xa0000000 ! P2 base address
|
||||
data_80000000: .long 0x80000000 ! P1 base address
|
||||
ccr_register_addr: .long 0xff00001c ! SH7305.CACHE.CCR register address
|
||||
ccr_register_data: .long 0x0000010f ! CCR configuration
|
||||
real_bootloader_start:
|
||||
.long ___fxcg50_bootloader_start
|
|
@ -0,0 +1,130 @@
|
|||
#include "fx9860/asm_utils.h"
|
||||
|
||||
.section .bootloader.text, "ax"
|
||||
|
||||
/* ___fxcg50_bootloader_start() : real bootstrap entry
|
||||
|
||||
Now we are in the URAM we can performs ASLR patching, setup stack and involve
|
||||
the first high-level C code which will perform kernel setup.
|
||||
|
||||
The primary (fake) bootloader (previous operations) have setup some arg:
|
||||
- r4 = ROM relocation base address
|
||||
- r5 = RAM relocation base address (physical)
|
||||
- r6 = image size */
|
||||
|
||||
function(__fxcg50_bootloader_start):
|
||||
|
||||
! ---
|
||||
! prepare alias
|
||||
! ---
|
||||
|
||||
#define rom_reloc_base r4
|
||||
#define ram_reloc_base r5
|
||||
#define image_size r6
|
||||
|
||||
! ---
|
||||
! ASLR application
|
||||
!
|
||||
! perform ASLR patch by using the symbols table information injected
|
||||
! during bootloader build steps at the end of the bootloader code marked
|
||||
! with ___bootloader_code_end.
|
||||
!
|
||||
! The content of the table has been generated during post-compiling script
|
||||
! ---
|
||||
|
||||
! The table symbol is not aready resolved (its our job), we must manually
|
||||
! calculate the real address of the symbols table
|
||||
mov.l bootloader_code_end, r0
|
||||
mov.l p1_addr_base, r1
|
||||
mov.l p2_addr_base, r2
|
||||
or ram_reloc_base, r2
|
||||
or ram_reloc_base, r1
|
||||
add r2, r0
|
||||
|
||||
! walk trough the symbols table and patch all location
|
||||
! @note
|
||||
! - we MUST perform patching using P2 (uncachable) area to avoid
|
||||
! inconsistancy behaviour with the cache.
|
||||
! - symbols are relocalize through P1 (cachable) area
|
||||
aslr_symbol_patch_loop:
|
||||
mov.l @r0, r8
|
||||
tst r8, r8
|
||||
bt aslr_commit
|
||||
add r2, r8
|
||||
mov.l @r8, r9
|
||||
add r1, r9
|
||||
mov.l r9, @r8
|
||||
mov.l r8, @r0
|
||||
add #4, r0
|
||||
bra aslr_symbol_patch_loop
|
||||
nop
|
||||
|
||||
aslr_commit:
|
||||
! Now that ASLR symbols has been updated using uncachable area (P2), we
|
||||
! need to invalitate all Operands Cache entry that the MPU have possibly
|
||||
! setup to avoid inconsistant `mov.x` behaviour
|
||||
! @note
|
||||
! - CCR.OCI = 1 -> Operand Cache Invalidation (self-cleared to 0)
|
||||
mov.l ccr_reg_addr, r1
|
||||
mov.l @r1, r0
|
||||
or #0x08, r0
|
||||
mov.l r0, @r1
|
||||
synco
|
||||
|
||||
setup_stack:
|
||||
! TODO : watermark stack area for statistics
|
||||
! TODO : stack switch
|
||||
! TODO : stack canary
|
||||
|
||||
bootloader_c_invokation:
|
||||
mov.l p2_addr_base, r2
|
||||
mov ram_reloc_base, r4
|
||||
mov image_size, r5
|
||||
mov.l bootloader_main, r0
|
||||
jsr @r0
|
||||
or r2, r4
|
||||
|
||||
! ---
|
||||
! bootloader panic
|
||||
!
|
||||
! As we have probably wierdly restored hadware information, if the
|
||||
! bootloader main routine return we simply display black screen. You can
|
||||
! uncomment following instruction to allows getkey() to return to the menu
|
||||
! (debug only)
|
||||
! ---
|
||||
|
||||
bootloader_paniktamer:
|
||||
mov.l syscall_trampoline, r8
|
||||
mov #0x4a, r4
|
||||
mov #3, r5
|
||||
mov.l syscall_id, r0
|
||||
jsr @r8
|
||||
nop
|
||||
test1:
|
||||
! add #-4, r15
|
||||
! mov r15, r4 ! column
|
||||
! add #-4, r15
|
||||
! mov r15, r5 ! row
|
||||
! add #-4, r15
|
||||
! mov r15, r1 ! keycode
|
||||
! mov #0, r6 ! type of waiting (KEYWAIT_HALTON_TIMEOFF)
|
||||
! mov #0, r7 ! timeout period
|
||||
! mov.l r1, @-r15 ! keycode
|
||||
! mov #0, r2
|
||||
! mov.l r2, @-r15 ! [menu] key return to menu
|
||||
! mov.l getkey_id, r0
|
||||
! jsr @r8
|
||||
! nop
|
||||
bra test1
|
||||
nop
|
||||
|
||||
.balign 4
|
||||
|
||||
bootloader_main: .long _bootloader_main
|
||||
bootloader_code_end: .long ___bootloader_code_end
|
||||
syscall_trampoline: .long 0x80020070
|
||||
syscall_id: .long 0x0276
|
||||
p2_addr_base: .long 0xa0000000
|
||||
p1_addr_base: .long 0x80000000
|
||||
getkey_id: .long 0x000012bf ! GetKeyWait_OS syscall ID
|
||||
ccr_reg_addr: .long 0xff00001c ! SH7305.CACHE.CCR register address
|
|
@ -0,0 +1,27 @@
|
|||
VXDEV_TOOLCHAIN_PREFIX = 'sh-elf-vhex-'
|
||||
VXDEV_TOOLCHAIN_PROCESSOR = 'sh'
|
||||
VXDEV_TOOLCHAIN_ENDIANNESS = 'big'
|
||||
VXDEV_CFLAGS = [
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-D__SUPPORT_FXCG50__',
|
||||
'-ffreestanding',
|
||||
'-nostdlib',
|
||||
'-fPIE',
|
||||
'-O3',
|
||||
'-mb',
|
||||
'-m4-nofpu',
|
||||
'-fstrict-volatile-bitfields',
|
||||
]
|
||||
VXDEV_LDFLAGS = [
|
||||
'-static',
|
||||
'-Wl,-Map=map',
|
||||
'-Wl,--build-id=none',
|
||||
'-Wl,--emit-relocs',
|
||||
]
|
||||
VXDEV_LIBS = [
|
||||
'-lgcc',
|
||||
]
|
||||
VXDEV_ASSETS = [
|
||||
'font8x12',
|
||||
]
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef FXCG50_BOOTLOADER_ASM_UTILS_H
|
||||
#define FXCG50_BOOTLOADER_ASM_UTILS_H 1
|
||||
|
||||
/* function() : define a function using special indication
|
||||
*
|
||||
* - the function's name must start with a '_'
|
||||
* - the function should start in a 4-aligned address in order to benefit from
|
||||
* the ILP (Instruction Level Parallelism) */
|
||||
#define function(name) \
|
||||
.balign 4 ;\
|
||||
.global _ ## name ;\
|
||||
_ ## name
|
||||
|
||||
/* FXCG50_SYSCALL_TRAMPOLINE - Casio's syscall trampoline address */
|
||||
#define FXCG50_SYSCALL_TRAMPOLINE 0x80020070
|
||||
|
||||
/* syscall() : helper used to quickly define Casio syscall invokation
|
||||
*
|
||||
* - the syscall trampoline code is common for all syscall
|
||||
* - r0 contain the syscall ID */
|
||||
#define syscall(id) \
|
||||
mov.l 1f, r2 ;\
|
||||
mov.l 2f, r0 ;\
|
||||
jmp @r2 ;\
|
||||
nop ;\
|
||||
.balign 4 ;\
|
||||
1: .long FXCG50_SYSCALL_TRAMPOLINE ;\
|
||||
2: .long id
|
||||
|
||||
#endif /* FXCG50_BOOTLOADER_ASM_UTILS_H */
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef FXCG50_KEYCODE_H
|
||||
#define FXCG50_KEYCODE_H
|
||||
|
||||
/* KEYSCODE_GEN() : generate keycode */
|
||||
#define KEYCODE_GEN(row, column) \
|
||||
(((row & 0x0f) << 4) | ((column & 0x0f) << 0))
|
||||
|
||||
/* Define all keycode */
|
||||
typedef enum
|
||||
{
|
||||
KEY_F1 = 0x41,
|
||||
KEY_F2 = 0x42,
|
||||
KEY_F3 = 0x43,
|
||||
KEY_F4 = 0x44,
|
||||
KEY_F5 = 0x45,
|
||||
KEY_F6 = 0x46,
|
||||
|
||||
KEY_SHIFT = 0x49,
|
||||
KEY_OPTN = 0x4a,
|
||||
KEY_VARS = 0x4b,
|
||||
KEY_MENU = 0x4c,
|
||||
KEY_LEFT = 0x4d,
|
||||
KEY_UP = 0x4e,
|
||||
|
||||
KEY_ALPHA = 0x31,
|
||||
KEY_SQUARE = 0x32,
|
||||
KEY_POWER = 0x33,
|
||||
KEY_EXIT = 0x34,
|
||||
KEY_DOWN = 0x35,
|
||||
KEY_RIGHT = 0x36,
|
||||
|
||||
KEY_XOT = 0x39,
|
||||
KEY_LOG = 0x3a,
|
||||
KEY_LN = 0x3b,
|
||||
KEY_SIN = 0x3c,
|
||||
KEY_COS = 0x3d,
|
||||
KEY_TAN = 0x3e,
|
||||
|
||||
KEY_FRAC = 0x21,
|
||||
KEY_FD = 0x22,
|
||||
KEY_LEFTP = 0x23,
|
||||
KEY_RIGHTP = 0x24,
|
||||
KEY_COMMA = 0x25,
|
||||
KEY_ARROW = 0x26,
|
||||
|
||||
KEY_7 = 0x29,
|
||||
KEY_8 = 0x2a,
|
||||
KEY_9 = 0x2b,
|
||||
KEY_DEL = 0x2c,
|
||||
|
||||
KEY_4 = 0x11,
|
||||
KEY_5 = 0x12,
|
||||
KEY_6 = 0x13,
|
||||
KEY_MUL = 0x14,
|
||||
KEY_DIV = 0x15,
|
||||
|
||||
KEY_1 = 0x19,
|
||||
KEY_2 = 0x1a,
|
||||
KEY_3 = 0x1b,
|
||||
KEY_PLUS = 0x1c,
|
||||
KEY_MINUS = 0x1d,
|
||||
|
||||
KEY_0 = 0x01,
|
||||
KEY_DOT = 0x02,
|
||||
KEY_EXP = 0x03,
|
||||
KEY_NEG = 0x04,
|
||||
KEY_EXE = 0x05,
|
||||
|
||||
KEY_UNUSED = 0xff,
|
||||
KEY_NONE = 0xfe,
|
||||
} KEY_t;
|
||||
|
||||
#endif /* FXCG50_KEYCODE_H */
|
|
@ -0,0 +1,17 @@
|
|||
#include "bootloader/display.h"
|
||||
#include "bootloader/bios.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* _bios_dfont_get() : get font information */
|
||||
int _bios_dfont_get(struct font **font)
|
||||
{
|
||||
extern struct font font8x12;
|
||||
|
||||
if (font == NULL)
|
||||
return -1;
|
||||
*font = &font8x12;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include "bootloader/bios.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* _bios_dinfo() : get display information */
|
||||
void _bios_dinfo(size_t *width, size_t *height)
|
||||
{
|
||||
if (width != NULL)
|
||||
*width = 396;
|
||||
if (height != NULL)
|
||||
*height = 224;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootloader/bios.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* _bios_dpixel() : dpixel wrapper */
|
||||
void _bios_dpixel(int x, int y, int color)
|
||||
{
|
||||
extern uint16_t vram[];
|
||||
|
||||
if ((unsigned)x >= 396 || (unsigned)y >= 224)
|
||||
return;
|
||||
vram[(y * 396) + x] = color;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//---
|
||||
// Internals
|
||||
//---
|
||||
|
||||
/* Registers and operations */
|
||||
enum {
|
||||
device_code_read = 0x000,
|
||||
driver_output_control = 0x001,
|
||||
entry_mode = 0x003,
|
||||
display_control_2 = 0x008,
|
||||
low_power_control = 0x00b,
|
||||
|
||||
ram_address_horizontal = 0x200,
|
||||
ram_address_vertical = 0x201,
|
||||
write_data = 0x202,
|
||||
|
||||
horizontal_ram_start = 0x210,
|
||||
horizontal_ram_end = 0x211,
|
||||
vertical_ram_start = 0x212,
|
||||
vertical_ram_end = 0x213,
|
||||
};
|
||||
|
||||
|
||||
/* r61524_select() : selecte screen mode */
|
||||
static inline void r61524_select(uint16_t reg)
|
||||
{
|
||||
/* Clear RS and write the register number */
|
||||
*(volatile uint8_t *)0xa405013c &= ~0x10;
|
||||
__asm__ volatile ("synco"::);
|
||||
*(volatile uint16_t *)0xb4000000 = reg;
|
||||
__asm__ volatile ("synco"::);
|
||||
|
||||
/* Set RS back. We don't do this in read()/write() because the display
|
||||
driver is optimized for consecutive GRAM access. LCD-transfers will
|
||||
be faster when executing select() followed by several calls to
|
||||
write(). (Although most applications should use the DMA instead.) */
|
||||
*(volatile uint8_t *)0xa405013c |= 0x10;
|
||||
__asm__ volatile ("synco"::);
|
||||
}
|
||||
|
||||
/* r61524_read() : read information from screen */
|
||||
static inline uint16_t r61524_read(void)
|
||||
{
|
||||
return *(volatile uint16_t *)0xb4000000;
|
||||
}
|
||||
|
||||
/* r61524_write() : write information */
|
||||
static void r61524_write(uint16_t data)
|
||||
{
|
||||
*(volatile uint16_t *)0xb4000000 = data;
|
||||
}
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* expose VRAM here */
|
||||
uint16_t vram[396*224];
|
||||
|
||||
/* _bios_dupdate() : small R61524 driver */
|
||||
void _bios_dupdate(void)
|
||||
{
|
||||
/* Set the windows size */
|
||||
r61524_select(horizontal_ram_start);
|
||||
r61524_write(0);
|
||||
r61524_select(horizontal_ram_end);
|
||||
r61524_write(395);
|
||||
r61524_select(vertical_ram_start);
|
||||
r61524_write(0);
|
||||
r61524_select(vertical_ram_end);
|
||||
r61524_write(223);
|
||||
|
||||
/* Set the RAM position */
|
||||
r61524_select(ram_address_horizontal);
|
||||
r61524_write(0);
|
||||
r61524_select(ram_address_vertical);
|
||||
r61524_write(0);
|
||||
|
||||
/* Bind address 0xb4000000 to the data write command */
|
||||
r61524_select(write_data);
|
||||
|
||||
/* send VRAM */
|
||||
for (int i = 0; i < 396*224; ++i) {
|
||||
r61524_write(vram[i]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include "fxcg50/asm_utils.h"
|
||||
|
||||
.text
|
||||
|
||||
!---
|
||||
! Public
|
||||
!---
|
||||
|
||||
/* _bios_free() (free) : free allocated memory */
|
||||
function(_bios_free):
|
||||
syscall(0x1f42)
|
|
@ -0,0 +1,11 @@
|
|||
#include "fxcg50/asm_utils.h"
|
||||
|
||||
.text
|
||||
|
||||
!---
|
||||
! Public
|
||||
!---
|
||||
|
||||
/* _bios_malloc() (malloc) : allocate memory */
|
||||
function(_bios_malloc):
|
||||
syscall(0x1f44)
|
|
@ -0,0 +1,89 @@
|
|||
#include "bootloader/console.h"
|
||||
#include "fxcg50/keycode.h"
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* console_key_handle_special() : handle special key handling */
|
||||
// TODO
|
||||
// - F_UP -> history
|
||||
// - F_DOWN -> history
|
||||
int console_key_handle_special(struct console *console, int key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KEY_SHIFT:
|
||||
console->input.mode.shift ^= 1;
|
||||
return 1;
|
||||
case KEY_ALPHA:
|
||||
console->input.mode.maj ^= 1;
|
||||
return 1;
|
||||
case KEY_OPTN:
|
||||
console->input.mode.ctrl ^= 1;
|
||||
return 1;
|
||||
case KEY_DOT:
|
||||
console_buffer_in_insert(console, ' ');
|
||||
return 1;
|
||||
case KEY_DEL:
|
||||
console_buffer_in_remove(console);
|
||||
return 1;
|
||||
case KEY_EXE:
|
||||
console->input.mode.exit = 1;
|
||||
return 1;
|
||||
case KEY_LEFT:
|
||||
console_buffer_in_cursor_move(console, -1);
|
||||
return 1;
|
||||
case KEY_RIGHT:
|
||||
console_buffer_in_cursor_move(console, 1);
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* console_key_handle(): Update the internal buffer with the given key code */
|
||||
int console_key_handle(struct console *console, int key)
|
||||
{
|
||||
static const uint8_t keylist_alpha[] = {
|
||||
KEY_XOT, KEY_LOG, KEY_LN, KEY_SIN, KEY_COS, KEY_TAN,
|
||||
KEY_FRAC, KEY_FD, KEY_LEFTP, KEY_RIGHTP, KEY_COMMA, KEY_ARROW,
|
||||
KEY_7, KEY_8, KEY_9,
|
||||
KEY_4, KEY_5, KEY_6, KEY_MUL, KEY_DIV,
|
||||
KEY_1, KEY_2, KEY_3, KEY_PLUS, KEY_MINUS,
|
||||
KEY_0, 0xff
|
||||
};
|
||||
static const uint8_t keylist_num[] = {
|
||||
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4,
|
||||
KEY_5, KEY_6, KEY_7, KEY_8, KEY_9,
|
||||
KEY_PLUS, KEY_MINUS, KEY_MUL, KEY_DIV,
|
||||
KEY_LEFTP, KEY_RIGHTP, KEY_COMMA, KEY_POWER,
|
||||
KEY_DOT, KEY_FD, KEY_ARROW, 0xff
|
||||
};
|
||||
static const char keylist_num_char[] = "0123456789+-x/(),^.|_";
|
||||
const uint8_t *keycode_list;
|
||||
char character;
|
||||
int i;
|
||||
|
||||
/* Get the appropriate key list */
|
||||
keycode_list = keylist_alpha;
|
||||
if (console->input.mode.shift == 1)
|
||||
keycode_list = keylist_num;
|
||||
|
||||
/* Try to find the pressed key. */
|
||||
i = -1;
|
||||
while (keycode_list[++i] != 0xff && keycode_list[i] != key);
|
||||
if (keycode_list[i] != key)
|
||||
return 0;
|
||||
|
||||
/* handle mode then update the buffer */
|
||||
if (console->input.mode.shift == 0) {
|
||||
character = 'a' + i;
|
||||
if (console->input.mode.maj == 1)
|
||||
character = 'A' + i;
|
||||
} else {
|
||||
character = keylist_num_char[i];
|
||||
}
|
||||
console_buffer_in_insert(console, character);
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#include "bootloader/console.h"
|
||||
#include "fxcg50/keycode.h"
|
||||
|
||||
//---
|
||||
// Internlas
|
||||
//---
|
||||
|
||||
/* keysc_fetch() : scan the KEYSC hardware module */
|
||||
static int keysc_fetch(void)
|
||||
{
|
||||
uint16_t volatile *SH7305_KEYSC = (void*)0xa44b0000;
|
||||
int column;
|
||||
int key;
|
||||
int row;
|
||||
int registered;
|
||||
uint16_t udata;
|
||||
|
||||
/* update the keycache */
|
||||
row = 6;
|
||||
key = 0x5f;
|
||||
registered = 0x0000;
|
||||
while (--row >= 0)
|
||||
{
|
||||
column = 16;
|
||||
udata = SH7305_KEYSC[row];
|
||||
if (registered != 0x0000)
|
||||
continue;
|
||||
while (--column >= 0)
|
||||
{
|
||||
if ((udata & (0x8000 >> column)) != 0) {
|
||||
registered = KEYCODE_GEN(row, column);
|
||||
break;
|
||||
}
|
||||
key -= 1;
|
||||
}
|
||||
}
|
||||
return registered;
|
||||
}
|
||||
|
||||
//---
|
||||
// Public
|
||||
//---
|
||||
|
||||
/* console_key_get() : small one-shot key waiter */
|
||||
int console_key_get(void)
|
||||
{
|
||||
static uint16_t prev_key = 0xffff;
|
||||
|
||||
if (prev_key != 0xffff) {
|
||||
while (1) {
|
||||
if (prev_key != keysc_fetch())
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (1) {
|
||||
prev_key = keysc_fetch();
|
||||
if (prev_key != 0x0000)
|
||||
break;
|
||||
}
|
||||
return prev_key;
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
#include "fxcg50/asm_utils.h"
|
||||
|
||||
.section .bootloader.pre_text, "ax"
|
||||
|
||||
/* ___fxcg50_initialize() : bootstrap routine
|
||||
|
||||
We are currently virtualized as a common program (addin) at 0x00300000, but the
|
||||
code currently executed is physically fragmented through the ROM. So, we cannot
|
||||
perform ASLR self-relocation here.
|
||||
|
||||
The first thing we need to do is find the "user" RAM allocated by Casio (by
|
||||
analyzing the TLB) and then performs a "self-translation" into this place.
|
||||
After that, we will be able to perform ASLR self-relocation and classic
|
||||
"kernel" bootstrapping.. */
|
||||
|
||||
function(__fxcg50_primary_bootloader):
|
||||
|
||||
! ---
|
||||
! *CRITICAL*
|
||||
!
|
||||
! The next two instructions will be patched *DURING COMPILE TIME* with the
|
||||
! complete image size (bootloader, ASLR, kernel, OS,...) as follows:
|
||||
! > mov.l complet_image_size, r0
|
||||
! > nop
|
||||
! If not patched, the code will just return and indicate to Casio not to
|
||||
! restart the application.
|
||||
! ---
|
||||
|
||||
rts
|
||||
mov #1, r0
|
||||
bra translation_entry
|
||||
nop
|
||||
.long 0x00000000
|
||||
|
||||
translation_entry:
|
||||
|
||||
! ---
|
||||
! prepare alias
|
||||
!
|
||||
! We don't need to setup the stack because we are loaded from CasioABS
|
||||
! (Casio OS) as a simple program (addin) with a given stack, which we will
|
||||
! not use even if we use "special" registers such as r8, r9, ... as we will
|
||||
! be careful to never return from here.
|
||||
!
|
||||
! @note
|
||||
! - r0 is reused
|
||||
! - r1 is reused
|
||||
! - r2 is reused
|
||||
! - r7 is reused
|
||||
! - r14 is reused
|
||||
! ---
|
||||
|
||||
#define counter r0
|
||||
#define utlb_addr_val r1
|
||||
#define utlb_data_val r2
|
||||
#define uram_phys_addr r3
|
||||
#define rom_base_addr r4
|
||||
#define rom_image_size r5
|
||||
#define utlb_data_ptr r6
|
||||
#define utlb_addr_ptr r7
|
||||
#define uram_virt_ptr r8
|
||||
#define uram_phys_size r9
|
||||
#define utlb_valid_bit r10
|
||||
#define utlb_data_ptr_saved r11
|
||||
#define utlb_magic_array r12
|
||||
#define utlb_addr_ptr_saved r13
|
||||
#define utlb_data_ppn_mask r14
|
||||
|
||||
! ---
|
||||
! Save critical information
|
||||
!
|
||||
! Calculate the runtime loaded address of the addin and save the
|
||||
! compile-time image size information
|
||||
! ---
|
||||
|
||||
! save image size
|
||||
mov r0, rom_image_size
|
||||
|
||||
! precalculate runtime loaded address
|
||||
mova utlb_fetch_uram_info, r0
|
||||
add #(___fxcg50_primary_bootloader - utlb_fetch_uram_info), r0
|
||||
mov r0, rom_base_addr
|
||||
|
||||
! ---
|
||||
! Configure cache
|
||||
!
|
||||
! This is a critical part because we will move to the URAM and perform
|
||||
! self-patching symbols during our execution. So, we need to have a
|
||||
! consistent cache behavior to help the MPU follow our instructions.
|
||||
!
|
||||
! The SH7305 has a special register for that called Cache Control Register
|
||||
! (CCR), and it seems tobe the same as SH7724 (except that no official POC
|
||||
! or information can invalidate the instruction Cache (IC) without causing
|
||||
! the machine to crash):
|
||||
!
|
||||
! - Indicate that P1 area use the "Write-back" method
|
||||
! - Indicate that P0/P3 areas use the "Write-through" method
|
||||
! - Enable Operand Cache (OC)
|
||||
! - Enable Instruction Cache (IC)
|
||||
! - Invalidate all Operand Cache (OC) entries
|
||||
! ---
|
||||
|
||||
mov.l ccr_register_addr, r0
|
||||
mov.l ccr_register_data, r1
|
||||
mov.l r1, @r0
|
||||
synco
|
||||
|
||||
! ---
|
||||
! UTLB scan to find URAM information
|
||||
!
|
||||
! As explained above, we first need to find the user RAM given by Casio for
|
||||
! our application, as we know that is an unused and stable space for us.
|
||||
!
|
||||
! We will scan each TLB entry to find the user's RAM. The memory is
|
||||
! virtually mapped by Casio using the same historical virtual address:
|
||||
! 0x08100000. Also, all the given RAM is entierly (?) mapped by the
|
||||
! operating system. Thus, we walk through the TLB until we don't find the
|
||||
! next memory fragment; this will allow us to find the size of the RAM
|
||||
! (which varies between models and emulators).
|
||||
!
|
||||
! We will also take advantage of the fact that Casio *MAP* the virtual
|
||||
! address 0x00000000 (NULL) for no valid reason. So, if we find this
|
||||
! mapping, we will invalidate it to be sure that a NULL manipulated pointer
|
||||
! will cause a TLBmiss exception.
|
||||
!
|
||||
! TODO : invalidate NULL page
|
||||
! ---
|
||||
|
||||
utlb_fetch_uram_info:
|
||||
|
||||
! fetch external information
|
||||
mov.l data_00000100, utlb_valid_bit
|
||||
mov.l data_08100000, uram_virt_ptr
|
||||
mov.l data_f6000000, utlb_addr_ptr
|
||||
mov.l data_14100c0a, utlb_magic_array
|
||||
mov.l data_1ffffc00, utlb_data_ppn_mask
|
||||
mov.l data_f7000000, utlb_data_ptr
|
||||
|
||||
! prepare internal vars
|
||||
mov #0, counter
|
||||
mov #-1, uram_phys_addr
|
||||
mov #0, uram_phys_size
|
||||
mov utlb_data_ptr, utlb_data_ptr_saved
|
||||
mov utlb_addr_ptr, utlb_addr_ptr_saved
|
||||
|
||||
utlb_walk_loop:
|
||||
! validity check
|
||||
! @note
|
||||
! - check the validity bit for each UTLB data and address entry
|
||||
! - both data and address entry have the same Valid bit position
|
||||
mov.l @utlb_addr_ptr, utlb_addr_val
|
||||
tst utlb_valid_bit, utlb_addr_val
|
||||
bt utlb_walk_cond_check
|
||||
mov.l @utlb_data_ptr, utlb_data_val
|
||||
tst utlb_valid_bit, utlb_data_val
|
||||
bt.s utlb_walk_cond_check
|
||||
|
||||
! check VPN validity
|
||||
! @note
|
||||
! - "UTLB Address Array" (p239) - Figure 7.14
|
||||
! - we need to clear the first 10 bits of the fetched UTLB data to get the
|
||||
! the "real" virtual address (eliminate ASID, Dirty and Valid bits)
|
||||
shlr8 utlb_addr_val
|
||||
shlr2 utlb_addr_val
|
||||
shll8 utlb_addr_val
|
||||
shll2 utlb_addr_val
|
||||
cmp/eq uram_virt_ptr, utlb_addr_val
|
||||
bf.s utlb_walk_cond_check
|
||||
|
||||
! fetch the page size
|
||||
! @note
|
||||
! - "Unified TLB (UTLB) Configuration"(p221)
|
||||
! - page size is weird to calculate for many hardware reasons, and this code
|
||||
! is the literal translation of :
|
||||
! > size = ((data->SZ1 << 1) | data->SZ2) << 3;
|
||||
! > size = 1 << ((0x14100c0a >> size) & 0xff);
|
||||
mov #-1, r1
|
||||
mov utlb_data_val, r0
|
||||
tst #128, r0
|
||||
mov #-1, r7
|
||||
negc r1, r1
|
||||
tst #16, r0
|
||||
add r1, r1
|
||||
negc r7, r7
|
||||
or r7, r1
|
||||
mov utlb_magic_array,r7
|
||||
shll2 r1
|
||||
add r1, r1
|
||||
neg r1, r1
|
||||
shad r1, r7
|
||||
extu.b r7, r1
|
||||
mov #1, r7
|
||||
shld r1, r7
|
||||
|
||||
! update counter / information
|
||||
add r7, uram_phys_size
|
||||
add r7, uram_virt_ptr
|
||||
|
||||
! check if the URAM physical address is already set
|
||||
mov uram_phys_addr,r0
|
||||
cmp/eq #-1,r0
|
||||
bf utlb_page_found_restart
|
||||
|
||||
! calculate the physical address of the page (URAM)
|
||||
! @note
|
||||
! - "UTLB Data Array"(p240) - Figure 7.15
|
||||
! - to fetch the Physical Address, we just need to isolate the PPN
|
||||
and utlb_data_ppn_mask, utlb_data_val
|
||||
shlr8 utlb_data_val
|
||||
shlr2 utlb_data_val
|
||||
mov utlb_data_val, uram_phys_addr
|
||||
shll8 uram_phys_addr
|
||||
shll2 uram_phys_addr
|
||||
|
||||
utlb_page_found_restart:
|
||||
mov r13, utlb_addr_ptr
|
||||
mov r11, utlb_data_ptr
|
||||
bra utlb_walk_loop
|
||||
mov #0, counter
|
||||
|
||||
utlb_walk_cond_check:
|
||||
! update internal counter
|
||||
! @notes
|
||||
! - only 64 UTLB entry
|
||||
! - UTLB entry (for data and address) gap is 0x100
|
||||
mov.l data_00000100, r1
|
||||
add #1, counter
|
||||
cmp/eq #64, counter
|
||||
add r1, utlb_addr_ptr
|
||||
bf.s utlb_walk_loop
|
||||
add r1, utlb_data_ptr
|
||||
|
||||
! ---
|
||||
! Self-translation to URAM
|
||||
!
|
||||
! Now that we have the user RAM entry address (uram_phys_addr) and its size
|
||||
! (uram_phys_size), we can self-translate to this location using a dummy
|
||||
! byte-per-byte copy.
|
||||
!
|
||||
! Note that, for now, no random installation offset is performed since
|
||||
! predicting uncertain behavior is complex to choose for now.
|
||||
! ---
|
||||
|
||||
self_translation:
|
||||
! fetch bootloader ROM geometry information
|
||||
mov rom_base_addr, r0
|
||||
mov rom_image_size, r2
|
||||
|
||||
! generate uncachable P2 URAM address
|
||||
! TODO
|
||||
! - random offset
|
||||
! - check oversize
|
||||
mov.l data_a0000000, r1
|
||||
or uram_phys_addr, r1
|
||||
|
||||
! dump the complet image into URAM
|
||||
self_dump_uram:
|
||||
mov.b @r0, r14
|
||||
mov.b r14, @r1
|
||||
dt r2
|
||||
add #1, r0
|
||||
add #1, r1
|
||||
bf.s self_dump_uram
|
||||
nop
|
||||
|
||||
! Prepare the self-translation by calculating the new PC position using a
|
||||
! P1 address to allow caching to be performed.
|
||||
! @note
|
||||
! - ___fxcg50_bootloader_start is a global symbol compiled with NULL as the
|
||||
! base address. So, we don't have to calculate the gap between the start
|
||||
! of the ROM and the symbol.
|
||||
mov.l data_80000000, r1
|
||||
or uram_phys_addr, r1
|
||||
mov.l real_bootloader_start, r0
|
||||
add r1, r0
|
||||
|
||||
! self-translation
|
||||
mov rom_image_size, r6
|
||||
mov uram_phys_addr, r5
|
||||
mov rom_base_addr, r4
|
||||
jmp @r0
|
||||
nop
|
||||
|
||||
.balign 4
|
||||
|
||||
data_08100000: .long 0x08100000 ! Casio addin load virtual address
|
||||
data_f6000000: .long 0xf6000000 ! SH7305 UTLB Address address
|
||||
data_f7000000: .long 0xf7000000 ! SH7305 UTLB Data addresss
|
||||
data_14100c0a: .long 0x14100c0a ! Magic UTLB page size table
|
||||
data_1ffffc00: .long 0x1ffffc00 ! UTLB Address PPN mask
|
||||
data_00000100: .long 0x00000100 ! UTLB entry gap and UTLB validity bit
|
||||
data_a0000000: .long 0xa0000000 ! P2 base address
|
||||
data_80000000: .long 0x80000000 ! P1 base address
|
||||
ccr_register_addr: .long 0xff00001c ! SH7305.CACHE.CCR register address
|
||||
ccr_register_data: .long 0x0000010f ! CCR configuration
|
||||
real_bootloader_start:
|
||||
.long ___fxcg50_bootloader_start
|
|
@ -0,0 +1,130 @@
|
|||
#include "fxcg50/asm_utils.h"
|
||||
|
||||
.section .bootloader.text, "ax"
|
||||
|
||||
/* ___fxcg50_bootloader_start() : real bootstrap entry
|
||||
|
||||
Now we are in the URAM we can performs ASLR patching, setup stack and involve
|
||||
the first high-level C code which will perform kernel setup.
|
||||
|
||||
The primary (fake) bootloader (previous operations) have setup some arg:
|
||||
- r4 = ROM relocation base address
|
||||
- r5 = RAM relocation base address (physical)
|
||||
- r6 = image size */
|
||||
|
||||
function(__fxcg50_bootloader_start):
|
||||
|
||||
! ---
|
||||
! prepare alias
|
||||
! ---
|
||||
|
||||
#define rom_reloc_base r4
|
||||
#define ram_reloc_base r5
|
||||
#define image_size r6
|
||||
|
||||
! ---
|
||||
! ASLR application
|
||||
!
|
||||
! perform ASLR patch by using the symbols table information injected
|
||||
! during bootloader build steps at the end of the bootloader code marked
|
||||
! with ___bootloader_code_end.
|
||||
!
|
||||
! The content of the table has been generated during post-compiling script
|
||||
! ---
|
||||
|
||||
! The table symbol is not aready resolved (its our job), we must manually
|
||||
! calculate the real address of the symbols table
|
||||
mov.l bootloader_code_end, r0
|
||||
mov.l p1_addr_base, r1
|
||||
mov.l p2_addr_base, r2
|
||||
or ram_reloc_base, r2
|
||||
or ram_reloc_base, r1
|
||||
add r2, r0
|
||||
|
||||
! walk trough the symbols table and patch all location
|
||||
! @note
|
||||
! - we MUST perform patching using P2 (uncachable) area to avoid
|
||||
! inconsistancy behaviour with the cache.
|
||||
! - symbols are relocalize through P1 (cachable) area
|
||||
aslr_symbol_patch_loop:
|
||||
mov.l @r0, r8
|
||||
tst r8, r8
|
||||
bt aslr_commit
|
||||
add r2, r8
|
||||
mov.l @r8, r9
|
||||
add r1, r9
|
||||
mov.l r9, @r8
|
||||
mov.l r8, @r0
|
||||
add #4, r0
|
||||
bra aslr_symbol_patch_loop
|
||||
nop
|
||||
|
||||
aslr_commit:
|
||||
! Now that ASLR symbols has been updated using uncachable area (P2), we
|
||||
! need to invalitate all Operands Cache entry that the MPU have possibly
|
||||
! setup to avoid inconsistant `mov.x` behaviour
|
||||
! @note
|
||||
! - CCR.OCI = 1 -> Operand Cache Invalidation (self-cleared to 0)
|
||||
mov.l ccr_reg_addr, r1
|
||||
mov.l @r1, r0
|
||||
or #0x08, r0
|
||||
mov.l r0, @r1
|
||||
synco
|
||||
|
||||
setup_stack:
|
||||
! TODO : watermark stack area for statistics
|
||||
! TODO : stack switch
|
||||
! TODO : stack canary
|
||||
|
||||
bootloader_c_invokation:
|
||||
mov.l p2_addr_base, r2
|
||||
mov ram_reloc_base, r4
|
||||
mov image_size, r5
|
||||
mov.l bootloader_main, r0
|
||||
jsr @r0
|
||||
or r2, r4
|
||||
|
||||
! ---
|
||||
! bootloader panic
|
||||
!
|
||||
! As we have probably wierdly restored hadware information, if the
|
||||
! bootloader main routine return we simply display black screen. You can
|
||||
! uncomment following instruction to allows getkey() to return to the menu
|
||||
! (debug only)
|
||||
! ---
|
||||
|
||||
bootloader_paniktamer:
|
||||
mov.l syscall_trampoline, r8
|
||||
mov #0x4a, r4
|
||||
mov #3, r5
|
||||
mov.l syscall_id, r0
|
||||
jsr @r8
|
||||
nop
|
||||
test1:
|
||||
! add #-4, r15
|
||||
! mov r15, r4 ! column
|
||||
! add #-4, r15
|
||||
! mov r15, r5 ! row
|
||||
! add #-4, r15
|
||||
! mov r15, r1 ! keycode
|
||||
! mov #0, r6 ! type of waiting (KEYWAIT_HALTON_TIMEOFF)
|
||||
! mov #0, r7 ! timeout period
|
||||
! mov.l r1, @-r15 ! keycode
|
||||
! mov #0, r2
|
||||
! mov.l r2, @-r15 ! [menu] key return to menu
|
||||
! mov.l getkey_id, r0
|
||||
! jsr @r8
|
||||
! nop
|
||||
bra test1
|
||||
nop
|
||||
|
||||
.balign 4
|
||||
|
||||
bootloader_main: .long _bootloader_main
|
||||
bootloader_code_end: .long ___bootloader_code_end
|
||||
syscall_trampoline: .long 0x80020070
|
||||
syscall_id: .long 0x0276
|
||||
p2_addr_base: .long 0xa0000000
|
||||
p1_addr_base: .long 0x80000000
|
||||
getkey_id: .long 0x000012bf ! GetKeyWait_OS syscall ID
|
||||
ccr_reg_addr: .long 0xff00001c ! SH7305.CACHE.CCR register address
|
|
@ -0,0 +1,24 @@
|
|||
VXDEV_TOOLCHAIN_PREFIX = 'aarch64-linux-gnu-'
|
||||
VXDEV_TOOLCHAIN_PROCESSOR = 'aarch64'
|
||||
VXDEV_TOOLCHAIN_ENDIANNESS = 'little'
|
||||
VXDEV_CFLAGS = [
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-D__SUPPORT_RASPI3B__',
|
||||
'-ffreestanding',
|
||||
'-nostdlib',
|
||||
'-fPIE',
|
||||
'-O3',
|
||||
'-mlittle-endian',
|
||||
'-march=armv8-a',
|
||||
'-fstrict-volatile-bitfields',
|
||||
]
|
||||
VXDEV_LDFLAGS = [
|
||||
'-static',
|
||||
'-Wl,-Map=map',
|
||||
'-Wl,--build-id=none',
|
||||
'-Wl,--emit-relocs',
|
||||
]
|
||||
VXDEV_ASSETS = [
|
||||
'font8x12',
|
||||
]
|
|
@ -0,0 +1,130 @@
|
|||
#ifndef VXGOS_BOOTLOADER_RASPI3B_AUXILIARY_H
|
||||
#define VXGOS_BOOTLOADER_RASPI3B_AUXILIARY_H
|
||||
|
||||
#include "raspi3b/utils.h"
|
||||
|
||||
//---
|
||||
// BCM2837 Auxiliary peripheral
|
||||
//
|
||||
// @note
|
||||
// <> all of theses bits is based on the documentation on
|
||||
// https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf
|
||||
// <> we use MSB instead of LSB packed struct because of the architecture
|
||||
// note that this cannot be modified with compiler flags.
|
||||
//---
|
||||
|
||||
struct __bcm2837_aux
|
||||
{
|
||||
/* [0x00215000] pending interrupt flags */
|
||||
const u32_union(AUX_IRQ,
|
||||
uint32_t MiniUART :1; /* Enable Mini UART */
|
||||
uint32_t SPI1 :1; /* Enable SPI1 */
|
||||
uint32_t SPI2 :1; /* Enable SPI2 */
|
||||
uint32_t :29;
|
||||
);
|
||||
/* [0x00215004] enable auxialiary modules */
|
||||
u32_union(AUX_ENABLE,
|
||||
uint32_t MiniUART :1; /* Enable Mini UART */
|
||||
uint32_t SPI1 :1; /* Enable SPI1 */
|
||||
uint32_t SPI2 :1; /* Enable SPI2 */
|
||||
uint32_t :29;
|
||||
);
|
||||
pad(0x38);
|
||||
|
||||
//---
|
||||
// Mini UART register
|
||||
//---
|
||||
|
||||
/* [0x00215040] used to write or write data from the FIFOs. */
|
||||
u32_union(AUX_MU_IO,
|
||||
uint32_t DATA : 8;
|
||||
uint32_t :24;
|
||||
);
|
||||
/* [0x00215044] used to enable interrupts */
|
||||
u32_union(AUX_MU_IER,
|
||||
uint32_t RIE :1; /* Receive Interrupt Enable */
|
||||
uint32_t TIE :1; /* Transmit Interrupt Enable */
|
||||
uint32_t :30;
|
||||
);
|
||||
/* [0x00215048] Interrupt Status */
|
||||
u32_union(AUX_MU_IIR,
|
||||
uint32_t PENDING :1; /* =0 if pending interrupts */
|
||||
uint32_t TXFLUSH :1; /* Receiver holds valid byte */
|
||||
uint32_t RXFLUSH :1; /* Transmit holding register empty */
|
||||
uint32_t :3;
|
||||
uint32_t FIFO :2; /* FIFO enabled (TODO: check RX/TX) */
|
||||
uint32_t :24;
|
||||
);
|
||||
/* [0x0021504c] Control Line Data */
|
||||
u32_union(AUX_MU_LCR,
|
||||
uint32_t CHR :2; /* Character Length */
|
||||
uint32_t :4;
|
||||
uint32_t BREAK :1; /* Pull down the UART1_TX line */
|
||||
uint32_t DLAB :1; /* Give access to the Baudrate reg */
|
||||
uint32_t :24;
|
||||
);
|
||||
/* [0x00215050] Control the 'modem' signals */
|
||||
u32_union(AUX_MU_MCR,
|
||||
uint32_t :1;
|
||||
uint32_t RTS :1; /* Pull down the UART1_RTS line */
|
||||
uint32_t :30;
|
||||
);
|
||||
/* [0x00215054] Data Status */
|
||||
const u32_union(AUX_MU_LSR,
|
||||
uint32_t DRIE :1; /* Received Data Ready */
|
||||
uint32_t ORER :1; /* Overrrun Error */
|
||||
uint32_t :3;
|
||||
uint32_t TDFE :1; /* Transmitter Empty */
|
||||
uint32_t TDFI :1; /* Transmitter Idle */
|
||||
uint32_t :25;
|
||||
);
|
||||
/* [0x00215058] Data 'modem' status */
|
||||
const u32_union(AUX_MU_MSR,
|
||||
uint32_t :4;
|
||||
uint32_t CTS :1; /* Inverse of the UART1_CTS input */
|
||||
uint32_t :27;
|
||||
);
|
||||
/* [0x0021505c] Single Byte Storage */
|
||||
u32_union(AUX_MU_SCRATCH,
|
||||
uint32_t SCRATCH :8; /* SDC byte extra */
|
||||
uint32_t :24;
|
||||
);
|
||||
/* [0x00215060] Access to Extra features */
|
||||
u32_union(AUX_MU_CNTL,
|
||||
uint32_t RE :1; /* Receiver Enable */
|
||||
uint32_t TE :1; /* Transmitter Enable */
|
||||
uint32_t ERAF :1; /* Enable Rx Auto Flow-control */
|
||||
uint32_t ETAF :1; /* Enable Tx Auto Flow-control */
|
||||
uint32_t RTS_AUTO :2; /* Rx Auto Flow Level */
|
||||
uint32_t RTS_ASSERT :1; /* Rx RTS invert */
|
||||
uint32_t CTS_ASSERT :1; /* CTS invert */
|
||||
uint32_t :24;
|
||||
);
|
||||
/* [0x00215064] Internal Status */
|
||||
const u32_union(AUX_MU_STAT,
|
||||
uint32_t SYMA :1; /* Symbol Available */
|
||||
uint32_t SPCA :1; /* Tx can accept a least one byte */
|
||||
uint32_t RII :1; /* Rx is idle */
|
||||
uint32_t TII :1; /* Tx is idle */
|
||||
uint32_t ROVRE :1; /* Rx overrun */
|
||||
uint32_t TDFF :1; /* Tx FIFO is full */
|
||||
uint32_t RTS :1; /* status of the UART1_RTS line */
|
||||
uint32_t CTS :1; /* status of the UART1_CTS line */
|
||||
uint32_t TDFE :1; /* Tx FIFO is empty */
|
||||
uint32_t TEND :1; /* Tx End */
|
||||
uint32_t :6;
|
||||
uint32_t RFFL :4; /* Rx FIFO fill level */
|
||||
uint32_t :4;
|
||||
uint32_t TFFL :4; /* Tx FIFO fill level */
|
||||
uint32_t :4;
|
||||
);
|
||||
/* [0x00215068] baudrate counter */
|
||||
u32_union(AUX_MU_BAUD,
|
||||
uint32_t BAUD :16; /* Baudrate */
|
||||
uint32_t :16;
|
||||
);
|
||||
} VPACKED(4);
|
||||
|
||||
#define BCM2837_AUXILIARY (*((volatile struct __bcm2837_aux *)0x3f215000))
|
||||
|
||||
#endif /* VXGOS_BOOTLOADER_RASPI3B_AUXILIARY_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue