vxGOS - v0.7.0-11 : add fx9860 support

*add*
> [vxgos]
  | [fx9860] add vxsdk build entry
  | [fx9860] add G1A file generation
  | [fx9860] add ASLR support
  | [fx9860] add bios primitives
  | [fx9860] add new 3x5 font
  | [fx9860] add custom GCC toolchain dependency for vxSDK
> [sdk]
  | [converter] support assets filter

*update*
> [scripts]
  | [vxdev] proper support "per board" assets requirements declaration
> [bootloader]
  | [display] proper font BIOS-dependant (board-specific) interface

*fix*
> [sdk]
  | [converter] fix default font border size
  | [converter] remove useless core function exposition
> [bootloader]
  | [assets] fix font8x9 to font8x12 (the real size)
> [scripts]
  | [vxdev] convert assets only if needed
This commit is contained in:
Yann MAGNIN 2023-06-05 20:57:31 +02:00
parent cefccb8bb1
commit d596c74634
31 changed files with 1268 additions and 90 deletions

View File

@ -38,17 +38,22 @@ def bootloader_configure(build, version):
"(compiles.toml)"
)
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 += '--bootloader'
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}")
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'
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)

View File

@ -22,6 +22,7 @@ class CompilesInfo():
cflags = []
ldflags = []
libs = []
assets = []
#---
# Public
@ -46,5 +47,7 @@ def compiles_fetch(filename):
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

View File

@ -56,6 +56,7 @@ def assets_conv_cli(argv):
generator = 'os'
prefix_output = ''
prefix_assets = ''
assets_filter = []
for arg in argv:
if arg in ['-f', '--force']:
force = True
@ -66,6 +67,9 @@ def assets_conv_cli(argv):
if not prefix_assets:
prefix_assets = arg
continue
if arg.find('--filter=') == 0:
assets_filter = arg[9:].split(',')
continue
if prefix_output:
log.warn(f"previous output path ({prefix_output}) dropped")
prefix_output = arg
@ -81,5 +85,6 @@ def assets_conv_cli(argv):
os.path.abspath(prefix_assets),
os.path.abspath(prefix_output),
generator,
assets_filter,
force
)

View File

@ -1,72 +1,4 @@
"""
core.conv - Vhex converter module
"""
from core.assets import assets_generate
__all__ = [
'generate_assets'
]
#---
# Public
#---
def generate_assets(prefix_assets, prefix_src, force_generate=True):
"""Generate Vhex assets.
This function abstract the asset convertion for the Vhex Operating System.
It will walk througt the `<source_prefix>` folder and will try to find some
files named 'vxconv.txt' wich discribe assets information of a potential
project. Then it will use them to convert assets into an appropriate source
file in C without using any Vhex-specific function (so, you can use this
converter for other project).
The vxconv.txt file is structured like basic key/value file:
```
<exposed_symbols_name>:
type: <image type> (font, bitmap) - required
path: <image path> - required
...
<next_exposed_symbols_name>:
...
```
Each asset file description should have at least type and name information,
and each type have potentially its own requierements.
type = bitmap:
================================== ========================================
Keys name and value type Description
================================== ========================================
profile: <name> Select the bitmap pixel profile
| rgb4 | RGB 4 (indexed)
| rgb4a | RGBA 4 (indexed)
| rgb8 | RGB 8 (indexed)
| rgb8a | RGBA 8 (indexed)
| rgb16 | RGB 16 (5:R, 6:G, 5:B)
| rgb16a | RGBA 16 (5:R, 5:G, 5:B, 1:A)
================================== ========================================
type = font:
================================== ========================================
Keys name and value type Description
================================== ========================================
grid.size: 8x9 (widthxheight) caracter size in pixel
grid.padding: <pixel> space between caracter
grig.border: <pixel> space around grid
proportional: <true,false> caracter are cropped
line_height: <pixel> caracter line alignement
charset: <print,unicode> charset specification
================================== ========================================
@args:
> path (str) - the path to find assets
> source_prefix (str) - the path to generate image source file
@return:
> a list of string which represents all assets sources files path
"""
return assets_generate(prefix_assets, prefix_src, force_generate)

View File

@ -102,13 +102,20 @@ class _VxAsset():
# Public
#---
def assets_generate(prefix_assets, prefix_output, generator, force_generate):
def assets_generate(
prefix_assets,
prefix_output,
generator,
assets_filter,
force_generate
):
""" Walk through the assets prefix and generate all source file
@args
> prefix_asset (str) - prefix used for recursivly search for asset info
> prefix_output (str) - prefix used for the output of generated file
> generator (str) - selected generator
> assets_filter (list) - assets filter
> force_generate (bool) - force generate the source file
@return
@ -122,6 +129,8 @@ def assets_generate(prefix_assets, prefix_output, generator, force_generate):
with open(f"{root}/vxconv.toml", 'r', encoding='utf-8') as inf:
content = toml.loads(inf.read())
for asset_name in content:
if assets_filter and asset_name not in assets_filter:
continue
log.debug(f"converting {asset_name}...")
log.user(
_VxAsset(

View File

@ -28,7 +28,7 @@ def font_meta_fetch(asset):
'grid_size_x' : 0,
'grid_size_y' : 0,
'grid_padding' : 1,
'grid_border' : 1,
'grid_border' : 0,
'is_proportional' : False,
'line_height' : 0,
'char_spacing' : 1,

View File

@ -0,0 +1,112 @@
"""
g3a_generator - Casio G1A file generator
"""
import os
import sys
#---
# 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, "2012.0903.1652") # addin date
for i in range(0, 44):
_u08(addin, 0x03c + i, 0x00) # icon
_str(addin, 0x1d4, "VXGOS ") # add name
_u32(addin, 0x002e, 0x00000000) # filesize (post)
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) != 2:
print('missing <rawbin> or <output>')
sys.exit(84)
rawbin = b''
with open(argv[0], 'rb') as rawfile:
rawbin = rawfile.read()
addin = bytearray(0x200)
_generate_standar_header(addin)
_generate_addin_subheader(addin)
addin += rawbin
_generate_checksums(addin)
if os.path.exists(argv[1]):
os.remove(argv[1])
with open(argv[1], 'xb') as addinfile:
addinfile.write(addin)
return 0
#---
# Public
#---
if __name__ == '__main__':
sys.exit(_main(sys.argv[1:]))

View File

@ -0,0 +1,137 @@
"""
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.bzImage",
f"{prefix_build}/vxgos.g1a",
],
capture_output=False,
check=False
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,7 +1,16 @@
[font8x9]
path = 'font8x9.png'
[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

View File

@ -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',
]

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -0,0 +1,11 @@
#include "fx9860/asm_utils.h"
.text
!---
! Public
!---
/* _bios_free() (free) : free allocated memory */
function(_bios_free):
syscall(0xacc)

View File

@ -0,0 +1,11 @@
#include "fx9860/asm_utils.h"
.text
!---
! Public
!---
/* _bios_malloc() (malloc) : allocate memory */
function(_bios_malloc):
syscall(0xacd)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -21,3 +21,6 @@ VXDEV_LDFLAGS = [
VXDEV_LIBS = [
'-lgcc',
]
VXDEV_ASSETS = [
'font8x12',
]

View File

@ -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;
}

View File

@ -4,9 +4,15 @@
#include <stdint.h>
#include <stddef.h>
// opaque definition of font struct
struct font;
/* _bios_dinfo() : get display information */
extern void _bios_dinfo(size_t *width, size_t *height);
/* _bios_dfont_get() : get font information */
extern int _bios_dfont_get(struct font **font);
/* _bios_dpixel() : draw pixel */
extern void _bios_dpixel(int x, int y, int color);

View File

@ -8,10 +8,5 @@
/* dfont_get() : get font information */
int dfont_get(struct font **font)
{
extern struct font font8x9;
if (font == NULL)
return -1;
*font = &font8x9;
return 0;
return _bios_dfont_get(font);
}

View File

@ -3,11 +3,17 @@ name = 'vxkernel'
version = '0.7.0'
target = [
'fxcg50',
'fx9860',
'raspi3b',
]
[build]
build = '/usr/bin/env python3 ./scripts/vxdev build --verbose'
build = '/usr/bin/env python3 ./scripts/vxdev build'
[fxcg50.dependencies]
sh-elf-vhex = 'master@superh'
[fx9860.dependencies]
sh-elf-vhex = 'master@superh'