Merge pull request #20 from logicog/installer

WIP: Add initial installer code
This commit is contained in:
René van Dorst
2025-09-26 17:39:46 +00:00
committed by GitHub
4 changed files with 707 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
CODE_LOCATION=0x1000
INSTALLER_ADDRESS=0x1100
CC = sdcc
CC_FLAGS = -mmcs51
ASM = sdas8051
AFLAGS= -plosgff
SRCS = installer.c
OBJS = ${SRCS:.c=.rel}
all: updatebuilder rtlplayground.bin
updatebuilder: updatebuilder.c
gcc $^ -o $@
installer.rel: installer.c
$(CC) $(CC_FLAGS) --code-loc ${CODE_LOCATION} -c $<
%.rel: %.asm
${ASM} ${AFLAGS} $^
%.rel: %.c
$(CC) $(CC_FLAGS) -c $<
rtlinstaller.ihx: crtstart.rel $(OBJS)
$(CC) $(CC_FLAGS) -Wl-bHOME=${INSTALLER_ADDRESS} -Wl-r -o $@ $^
rtlplayground.bin: rtlinstaller.ihx ../rtlplayground.bin
cp ../rtlplayground.bin .
./updatebuilder -i $< rtlplayground.bin
clean:
rm *.ihx *.lst *.rel *.rst *.sym *.lst *.map *.mem *.lk $(OBJS) installer.asm
+26
View File
@@ -0,0 +1,26 @@
.globl __start__stack
;--------------------------------------------------------
; Stack segment in internal ram
;--------------------------------------------------------
.area SSEG (DATA)
__start__stack:
.ds 1
.area VECTOR (CODE)
.globl __interrupt_vect
__interrupt_vect:
ljmp __sdcc_gsinit_startup + 0x1000
ljmp _isr_ext0 ; 0x03
.ds 5
ljmp _isr_timer0 ; 0x0b
.ds 5
ljmp _isr_ext1 ; 0x13
.globl __start__stack
.area GSINIT0 (CODE)
__sdcc_gsinit_startup:
mov sp,#__start__stack - 1
.area GSFINAL (CODE)
ljmp _installer
+376
View File
@@ -0,0 +1,376 @@
#include <8051.h>
#include <stdint.h>
// #define REGDBG 1
// #define RXTXDBG 1
#define UPDATE_LOC 0x0001D000
#define HEADER_LENGTH 0x14
#define UPDATE_CODE_LOC (UPDATE_LOC + HEADER_LENGTH)
#include "../rtl837x_sfr.h"
#include "../rtl837x_regs.h"
#define SYS_TICK_HZ 100
#define SERIAL_BAUD_RATE 57600
#define CLOCK_HZ 125000000
// Derive the divider settings for the internal clock
#if CLOCK_HZ == 20800000
#define CLOCK_DIV 3
#elif CLOCK_HZ == 31250000
#define CLOCK_DIV 2
#elif CLOCK_HZ == 62500000
#define CLOCK_DIV 1
#elif CLOCK_HZ == 125000000
#define CLOCK_DIV 0
#endif
volatile __xdata uint32_t ticks;
volatile __xdata uint8_t sec_counter;
volatile __xdata uint16_t sleep_ticks;
// We buffer 1 sector as this is also the erase size
__xdata uint8_t buffer[0x1000];
__xdata uint8_t dio_enabled;
__code uint8_t * __code hex = "0123456789abcdef";
void isr_timer0(void) __interrupt(1)
{
TR0 = 0; // Stop timer 0
TH0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) >> 8;
TL0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) % 0xff;
ticks++;
if (sleep_ticks > 0)
sleep_ticks--;
sec_counter++;
TR0 = 1; // Re-start timer 0
}
void isr_ext0(void) __interrupt(0)
{
EX0 = 0; // Disable interrupt for the moment
IT0 = 1; // Trigger on falling edge of external interrupt
EX0 = 1; // Re-enable interrupt
}
void isr_ext1(void) __interrupt(2)
{
EX1 = 0;
EX1 = 1;
}
void write_char(char c)
{
do {
} while (TI == 0);
TI = 0;
if (c =='\n') {
SBUF = '\r';
do {
} while (TI == 0);
TI = 0;
}
SBUF = c;
}
void print_string(__code char *p)
{
while (*p)
write_char(*p++);
}
void print_byte(uint8_t a)
{
write_char(hex[(a >> 4) & 0xf]);
write_char(hex[a & 0xf]);
}
void print_short(uint16_t a)
{
print_string("0x");
for (signed char i = 12; i >= 0; i -= 4) {
write_char(hex[(a >> i) & 0xf]);
}
}
void setup_serial(void)
{
IE = 0;
T2CON = 0x34; // Enable RCLK/TCLK (serial transmit/receive clock for T2), TR2 (Timer 2 RUN), disable CP/RL2 (bit 0)
SCON = 0x50; // Mode = 1: ASYNC 8N1 with T2 as baud-rate generator, REN_0 Receive enable
// The RCAP2 registers contain the high/low byte that is loaded into
// timer2 when T2 overflows to 0x10000
RCAP2H = (0x10000 - (CLOCK_HZ / SERIAL_BAUD_RATE / 32)) >> 8;
RCAP2L = (0x10000 - (CLOCK_HZ / SERIAL_BAUD_RATE / 32)) % 0xff;
PCON |= 0x80; // Double the Baud Rate
SCON = 0x50;
TI = 1;
RI = 0;
ES = 1; // Enable serial IRQ
}
/*
* Configure Memory Managed IO
*/
void flash_configure_mmio(void)
{
// Set configuration for MMIO access by controller
if (dio_enabled) {
SFR_FLASH_MODEB = 0x18;
SFR_FLASH_CMD_R = 0xbb; // By default we read with Dual speed
SFR_FLASH_DUMMYCYCLES = 4;
return;
}
SFR_FLASH_MODEB = 0x0;
SFR_FLASH_CMD_R = 0xb; // By default we read with single speed
SFR_FLASH_DUMMYCYCLES = 8;
}
/*
* Initializes the flash controller for programmed control
* The configuration options are not really understood, the SPI speed
* seems to be directly linked to the CPU frequency
* This configures fast single IO at 20.8 MHz when the CPU clock is at 20.8MHz
* and 62.5MHz when the CPU clock is configured at 125MHz
*/
void flash_init(uint8_t enable_dio)
{
if (enable_dio) {
// Configure fast DIO via divider/DIO/SIOconfig = 4 and read-cmd being 0xbb (for mmio)
SFR_FLASH_CONFIG = 9; // There may be a chip-select in here
SFR_FLASH_CONF_RCMD = 0xbb;
SFR_FLASH_CONF_DIV = 4;
} else {
// Configure fast read via divider = 8 and read-cmd being 0xb (for mmio)
SFR_FLASH_CONFIG = 9;
SFR_FLASH_CONF_RCMD = 0xb;
SFR_FLASH_CONF_DIV = 8;
}
// Test Controller Busy
while(SFR_FLASH_EXEC_BUSY);
// Write 0 to status register
SFR_FLASH_DUMMYCYCLES = 8;
SFR_FLASH_MODEB = 0;
SFR_FLASH_TCONF = 0x19;
SFR_FLASH_CMD = 1;
SFR_FLASH_DATA0 = 0;
SFR_FLASH_EXEC_GO = 1;
while(SFR_FLASH_EXEC_BUSY);
dio_enabled = enable_dio;
flash_configure_mmio();
}
uint8_t flash_read_status(void)
{
// Test Controller Busy (we might call this directly after executing a command)
while(SFR_FLASH_EXEC_BUSY);
// setup status read command
SFR_FLASH_TCONF = 0x11;
SFR_FLASH_CMD_R = 5;
// execute and wait for controller done
SFR_FLASH_EXEC_GO = 1;
while(SFR_FLASH_EXEC_BUSY);
return SFR_FLASH_DATA0;
}
/*
* Reads bulk data of length len from the flash memory starging at address src
* and writes the data into a buffer pointed to by dst in XMEM
*/
void flash_read_bulk(register __xdata uint8_t *dst, __xdata uint32_t src, register uint16_t len)
{
short status;
do {
status = flash_read_status();
} while (status & 0x1);
// Set fast read mode
if (dio_enabled) {
SFR_FLASH_MODEB = 0x18;
SFR_FLASH_CMD_R = 0xbb;
SFR_FLASH_DUMMYCYCLES = 4;
} else {
SFR_FLASH_MODEB = 0x0;
SFR_FLASH_CMD_R = 0xb; // Fast read
SFR_FLASH_DUMMYCYCLES = 8; // Add 8 dummy clocks after read?
}
// Read 4 bytes
SFR_FLASH_TCONF = 4;
while (len) {
SFR_FLASH_ADDR16 = src >> 16;
SFR_FLASH_ADDR8 = src >> 8;
SFR_FLASH_ADDR0 = src;
src += 4;
SFR_FLASH_EXEC_GO = 1;
while(SFR_FLASH_EXEC_BUSY);
*dst++ = SFR_FLASH_DATA0;
if (len == 1)
return;
*dst++ = SFR_FLASH_DATA8;
if (len == 2)
return;
*dst++ = SFR_FLASH_DATA16;
if (len == 3)
return;
*dst++ = SFR_FLASH_DATA24;
len -= 4;
}
}
void flash_write_enable(void)
{
short status;
// Wait until busy bit clear
do {
status = flash_read_status();
} while (status & 0x1);
// while (flash_read_status() & 0x1);
SFR_FLASH_TCONF = 0x18;
SFR_FLASH_CMD = 6;
// The following is explicitly set for SIO, is this necessary?:
SFR_FLASH_DUMMYCYCLES = 0;
SFR_FLASH_MODEB = 0;
SFR_FLASH_EXEC_GO = 1;
// Wait for write status enabled
do {
status = flash_read_status();
} while (!(status & 0x2));
}
// Erases the 4k sector in which the address lies
void flash_sector_erase(uint32_t addr)
{
flash_write_enable();
SFR_FLASH_TCONF = 8;
SFR_FLASH_CMD = 0x20;
SFR_FLASH_ADDR16 = addr >> 16;
SFR_FLASH_ADDR8 = addr >> 8;
SFR_FLASH_ADDR0 = addr;
SFR_FLASH_EXEC_GO = 1;
while (flash_read_status() & 0x1);
flash_configure_mmio();
}
void flash_write_bytes(__xdata uint32_t addr, __xdata uint8_t *ptr, uint16_t len)
{
uint8_t exit_loop = 0;
while(1) {
flash_write_enable();
SFR_FLASH_CMD = 2;
SFR_FLASH_TCONF = 0x40 | 8 | 4; // Bytes written is 4, 8 enables write, 0x40 is unknown
// Last transfer?
if (len < 5) {
SFR_FLASH_TCONF = 8 | len;
exit_loop = 1;
}
SFR_FLASH_ADDR16 = addr >> 16;
SFR_FLASH_ADDR8 = addr >> 8;
SFR_FLASH_ADDR0 = addr;
SFR_FLASH_DATA0 = *ptr++;
SFR_FLASH_DATA8 = *ptr++;
SFR_FLASH_DATA16 = *ptr++;
SFR_FLASH_DATA24 = *ptr++;
// Execute transfer, we wait for completion at top of loop
SFR_FLASH_EXEC_GO = 1;
if (exit_loop)
break;
len -= 4;
addr += 4;
}
while (flash_read_status() & 0x1);
flash_configure_mmio();
}
void reg_write(uint16_t reg_addr)
{
/* Data to write must be in SFR A4, A5, A6, A7 */
SFR_REG_ADDR_U16 = reg_addr;
SFR_EXEC_GO = SFR_EXEC_WRITE_REG;
do {
} while (SFR_EXEC_STATUS != 0);
}
void installer(void)
{
CKCON = 0; // Initial Clock configuration
SFR_97 = 0; // HADDR?
// Set in managed mode:
SFR_b9 = 0x00;
SFR_ba = 0x80;
// Disable all interrupts (global and individually) by setting IE register (SFR A8) to 0
IE = 0;
EIE = 0; // SFR e8: EIE. Disable all external IRQs
// Disable all interrupts (global interrupt enable bit)
EA = 0; // SFR A8.7 / IE.7
setup_serial();
print_string("\nRTLPlayground installer starting...\n");
// Initialize flash functions with disable DIO because writing does not work otherwise
flash_init(0);
__xdata uint32_t dest = 0x0;
__xdata uint32_t source = UPDATE_CODE_LOC;
// A 512kByte = 4MBit Flash has 128 sectors, we copy only 120
for (uint8_t i=0; i < 120; i++) {
print_string("Moving block\n");
flash_read_bulk(buffer, source, 0x1000);
for (uint8_t j = 0; j < 32; j++) {
write_char(' '); print_byte(buffer[j]);
}
write_char('\n');
flash_sector_erase(dest);
flash_write_bytes(dest, buffer, 0x1000);
dest += 0x1000;
source += 0x1000;
}
print_string("Done.\n");
print_string("Reseting now\n");
REG_SET(RTL837X_REG_RESET, 1);
}
+271
View File
@@ -0,0 +1,271 @@
/*
* Adds data files into specified locations of an image, optionally creates
* an index in the form of a header file
*/
#include <stdint.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <argp.h>
#include <stdbool.h>
#define HEADER_LENGTH 0x14
#define HEADER_MAGIC 0x12345678
#define HEADER_RESERVED 0x332255ff
#define SEG_01002_LENGTH 0x2ffe
#define SEG_1C000_LENGTH 0x1000
#define SEG_1d000_OFFSET 0x3ffe
// Use a 4MB buffer, the maximum flash rom size
#define BUFFER_SIZE 0x400000
uint8_t buffer[BUFFER_SIZE];
FILE *inptr;
int outptr;
char line[256];
const char *argp_program_version = "updatebuilder 0.1";
const char *argp_program_bug_address = "<git@logicog.de>";
static char doc[] = "Create an update image for RTL837X-based switches";
static char args_doc[] = "INPUT_IMAGE";
static struct argp_option options[] = {
{ "magic", 'm', "MAGIC", 0, "Magic number"},
{ "reserved", 'r', "MAGIC", 0, "Reserved number"},
{ "installer", 'i', "FILE", 0, "Installer file"},
{ "output", 'o', "FILE", 0, "Output file"},
{ 0 }
};
struct arguments {
uint32_t reserved;
uint32_t magic;
char *installer_file;
char *output_file;
};
int get_byte(int pos)
{
int c1 = line[pos];
if (c1 >= '0' && c1 <= '9')
c1 -= '0';
else if (c1 >= 'a' && c1 <= 'f')
c1 = c1 - 'a' + 10;
else if (c1 >= 'A' && c1 <= 'F')
c1 = c1 - 'A' + 10;
else
return -1;
int c2 = line[pos + 1];
if (c2 >= '0' && c2 <= '9')
c2 -= '0';
else if (c2 >= 'a' && c2 <= 'f')
c2 = c2 - 'a' + 10;
else if (c2 >= 'A' && c2 <= 'F')
c2 = c2 - 'A' + 10;
else
return -1;
return c1 << 4 | c2;
}
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
struct arguments *arguments = state->input;
switch (key) {
case 'm':
arguments->magic = arg? strtol(arg, NULL, 16): HEADER_MAGIC;
break;
case 'r':
arguments->reserved = arg? strtol(arg, NULL, 16): HEADER_RESERVED;
break;
case 'i':
arguments->installer_file = arg;
break;
case 'o':
arguments->output_file = arg;
break;
case ARGP_KEY_END:
if(state->arg_num < 1) // Expect 1 command line argument at end
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = {
options, parse_opt, args_doc, doc, 0, 0, 0
};
int main(int argc, char **argv)
{
struct arguments arguments;
int arg_index;
char tmpfilename[] = "image_XXXXXX";
arguments.reserved = HEADER_RESERVED;
arguments.magic = HEADER_MAGIC;
arguments.output_file = NULL;
argp_parse(&argp, argc, argv, 0, &arg_index, &arguments);
memset(buffer, 0, BUFFER_SIZE);
size_t filesize = 0;
inptr = fopen(argv[arg_index], "rb");
if (inptr == NULL) {
printf("Cannot open input file %s\n", argv[arg_index]);
return 5;
}
fseek(inptr, 0L, SEEK_END);
filesize = ftell(inptr);
rewind(inptr);
printf("Input file size: %ld\n", filesize);
if (filesize > BUFFER_SIZE) {
printf("File too large.\n");
return 5;
}
size_t bytes_read = fread(buffer + SEG_1d000_OFFSET + 2 * HEADER_LENGTH, 1, sizeof(buffer), inptr);
printf("Bytes read: %ld\n", bytes_read);
if (bytes_read != filesize) {
printf("Error reading input file.\n");
return 5;
}
fclose(inptr);
filesize += SEG_1d000_OFFSET + 2 * HEADER_LENGTH;
// Read the installer file, which is in Intel Hex format
if (arguments.installer_file) {
inptr = fopen(arguments.installer_file, "rb");
if (inptr == NULL) {
printf("Cannot open installer file %s\n", arguments.installer_file);
return 5;
}
int line_num = 1;
int address_high = 0;
bool eof = false;
while(fgets(line, 255, inptr)) {
if (line[0] != ':') {
printf("Unknwon installer file format for %s\n", arguments.installer_file);
return 5;
}
int bytes = get_byte(1);
if (bytes < 0 || bytes > 200) {
printf("Error in %s, line %d, incorrect byte number\n", arguments.installer_file, line_num);
return 5;
}
int address = (get_byte(3) *256) + get_byte(5);
if (address < 0) {
printf("Error in %s, line %d, not an address\n", arguments.installer_file, line_num);
return 5;
}
int type = get_byte(7);
if (type < 0 || type > 5) {
printf("Error in %s, line %d incorrect type\n", arguments.installer_file, line_num);
return 5;
}
if (type == 0) {
for (int i = 0; i < bytes; i++) {
int data = get_byte(9 + 2 * i);
if (data < 0) {
printf("Error in %s, line %d, illegal data byte\n", arguments.installer_file, line_num);
return 5;
} else {
address = address > 0x1000 ? address - 0x1000 : address;
buffer[address + HEADER_LENGTH + i] = data;
}
}
} else if (type == 1) {
eof = true;
printf("EOF\n");
} else {
printf("UNKNOWN type, line %d\n", line_num);
}
line_num++;
}
if (!eof)
printf("Something was wrong: EOF not found\n");
fclose(inptr);
}
/*
* Fill in the header with the magic, file-length, header sum, payload sum
* and the reserved bytes
*/
*(uint32_t *)(buffer + 0x00) = htonl(arguments.magic);
*(uint32_t *)(buffer + 0x04) = htonl(filesize - HEADER_LENGTH);
uint32_t sum = 0;
// for (int i = HEADER_LENGTH; i < filesize; i++)
// sum += buffer[i];
for (int i = HEADER_LENGTH; i < SEG_01002_LENGTH; i++)
sum += buffer[i];
printf("Payload sum 1 is: 0x%x\n", sum);
for (int i = HEADER_LENGTH + SEG_01002_LENGTH; i < SEG_01002_LENGTH + SEG_1C000_LENGTH + HEADER_LENGTH; i++)
sum += buffer[i];
printf("Payload sum 2 is: 0x%x\n", sum);
sum += 0xff * HEADER_LENGTH;
printf("Payload sum with header is: 0x%x\n", sum);
for (int i = 2 * HEADER_LENGTH + SEG_01002_LENGTH + SEG_1C000_LENGTH; i < filesize; i++)
sum += buffer[i];
printf("Payload sum is: 0x%x\n", sum);
*(uint32_t *)(buffer + 0x0c) = htonl(sum);
*(uint32_t *)(buffer + 0x10) = htonl(arguments.reserved);
sum = 0;
for (int i = 0; i < HEADER_LENGTH; i++)
sum += buffer[i];
printf("Header checksum is: 0x%x\n", sum);
*(uint32_t *)(buffer + 0x08) = htonl(sum);
// Second header is copy of initial one:
*(uint32_t *)(buffer + 0x00 + HEADER_LENGTH + SEG_1d000_OFFSET) = *(uint32_t *)(buffer + 0x00);
*(uint32_t *)(buffer + 0x04 + HEADER_LENGTH + SEG_1d000_OFFSET) = *(uint32_t *)(buffer + 0x04);
*(uint32_t *)(buffer + 0x08 + HEADER_LENGTH + SEG_1d000_OFFSET) = *(uint32_t *)(buffer + 0x08);
*(uint32_t *)(buffer + 0x0c + HEADER_LENGTH + SEG_1d000_OFFSET) = *(uint32_t *)(buffer + 0x0c);
*(uint32_t *)(buffer + 0x10 + HEADER_LENGTH + SEG_1d000_OFFSET) = *(uint32_t *)(buffer + 0x10);
if (filesize) {
if (arguments.output_file)
outptr = creat(arguments.output_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
else
outptr = mkstemp(tmpfilename);
if (!outptr) {
printf("Cannot open %s\n", arguments.output_file ? arguments.output_file : tmpfilename);
return 5;
}
size_t written = write(outptr, buffer, filesize);
if (written != filesize) {
printf("Error writing output file.\n");
return 5;
}
close(outptr);
if (!arguments.output_file)
rename(tmpfilename, argv[arg_index]);
}
return 0;
}