rtl8195am - refactor bootloader and ota support

1. move ota region 1 from 0x00b000 to 0x040000
2. move ota region 2 from 0x0c0000 to 0x120000
3. refactor bootloader header as follows:

   uint32_t tag;
   uint32_t ver;
   uint64_t timestamp;
   uint32_t size;
   uint8_t hash[32];
   uint8_t campaign[16];
   uint32_t crc32;

   where,
   a. hash is the sha256 checksum of the payload.
   b. crc32 is the crc32 checksum of headers from tag to campaign.

4. Call NVIC_SystemReset for soft reset.

Signed-off-by: Tony Wu <tung7970@gmail.com>
pull/5531/head
Tony Wu 2017-11-12 22:46:32 +08:00
parent 985c2b0ec7
commit a4575a965f
6 changed files with 235 additions and 128 deletions

View File

@ -43,8 +43,7 @@
#include "rtl8195a_compiler.h" #include "rtl8195a_compiler.h"
#include "rtl8195a_platform.h" #include "rtl8195a_platform.h"
#include "rtl8195a_crypto.h"
#define REG32(reg) (*(volatile uint32_t *)(reg)) #define REG32(reg) (*(volatile uint32_t *)(reg))
#define REG16(reg) (*(volatile uint16_t *)(reg)) #define REG16(reg) (*(volatile uint16_t *)(reg))

View File

@ -0,0 +1,24 @@
/* mbed Microcontroller Library
* Copyright (c) 2013-2017 Realtek Semiconductor Corp.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_RTL8195A_CRYPTO_H
#define MBED_RTL8195A_CRYPTO_H
extern _LONG_CALL_ uint32_t crc32_get(uint8_t *buf, int len);
#endif

View File

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include <stdio.h>
#include <string.h> #include <string.h>
#include "mbed_wait_api.h" #include "mbed_wait_api.h"
@ -24,61 +23,105 @@
static flash_t flash_obj; static flash_t flash_obj;
void OTA_GetImageInfo(imginfo_t *info) void OTA_ReadHeader(uint32_t base, imginfo_t *img)
{ {
uint32_t ver_hi, ver_lo; uint32_t epoch_hi, epoch_lo;
flash_ext_read_word(&flash_obj, info->base + TAG_OFS, &info->tag); if (base != OTA_REGION1_BASE || base != OTA_REGION2_BASE) {
flash_ext_read_word(&flash_obj, info->base + VER_OFS, &ver_lo); return;
flash_ext_read_word(&flash_obj, info->base + VER_OFS + 4, &ver_hi); }
if (info->tag == TAG_DOWNLOAD) { flash_ext_read_word(&flash_obj, base + OTA_TAG_OFS, &img->tag);
info->ver = ((uint64_t)ver_hi << 32) | (uint64_t) ver_lo; flash_ext_read_word(&flash_obj, base + OTA_VER_OFS, &img->ver);
flash_ext_read_word(&flash_obj, base + OTA_EPOCH_OFS, &epoch_hi);
flash_ext_read_word(&flash_obj, base + OTA_EPOCH_OFS + 4, &epoch_lo);
img->timestamp = ((uint64_t)epoch_hi << 32) | (uint64_t) epoch_lo;
flash_ext_read_word(&flash_obj, base + OTA_SIZE_OFS, &img->size);
flash_ext_stream_read(&flash_obj, base + OTA_HASH_OFS, 32, img->hash);
flash_ext_stream_read(&flash_obj, base + OTA_CAMPAIGN_OFS, 16, img->campaign);
flash_ext_read_word(&flash_obj, base + OTA_CRC32_OFS, &img->crc32);
}
bool OTA_CheckHeader(imginfo_t *img)
{
uint8_t *msg;
uint32_t crc;
msg = (uint8_t *)img;
crc = crc32_get(msg, OTA_CRC32_LEN);
if (crc != img->crc32) {
return false;
}
if ((img->tag & OTA_TAG_CHIP_MSK) != (OTA_TAG_ID & OTA_TAG_CHIP_MSK)) {
return false;
}
return true;
}
void OTA_GetImageInfo(uint32_t base, imginfo_t *img)
{
OTA_ReadHeader(base, img);
if (!OTA_CheckHeader(img)) {
img->timestamp = 0;
img->valid = false;
}
img->valid = true;
}
uint32_t OTA_GetUpdateBase(void)
{
imginfo_t img1, img2;
OTA_GetImageInfo(OTA_REGION1_BASE, &img1);
OTA_GetImageInfo(OTA_REGION2_BASE, &img2);
if (img1.valid && img2.valid) {
if (img1.timestamp < img2.timestamp) {
return OTA_REGION1_BASE;
} else { } else {
info->ver = 0; return OTA_REGION2_BASE;
} }
} }
uint32_t OTA_GetBase(void) if (img1.valid) {
return OTA_REGION2_BASE;
}
return OTA_REGION1_BASE;
}
uint32_t OTA_UpateHeader(uint32_t base, imginfo_t *img)
{ {
static uint32_t ota_base = 0; flash_ext_write_word(&flash_obj, base + OTA_TAG_OFS, img->tag);
imginfo_t region1, region2; flash_ext_write_word(&flash_obj, base + OTA_VER_OFS, img->ver);
flash_ext_write_word(&flash_obj, base + OTA_EPOCH_OFS, img->timestamp >> 32);
flash_ext_write_word(&flash_obj, base + OTA_EPOCH_OFS + 4, (img->timestamp << 32) >> 32);
if (ota_base == OTA_REGION1 || ota_base == OTA_REGION2) { flash_ext_write_word(&flash_obj, base + OTA_SIZE_OFS, img->size);
return ota_base; flash_ext_stream_write(&flash_obj, base + OTA_HASH_OFS, 32, img->hash);
flash_ext_stream_write(&flash_obj, base + OTA_CAMPAIGN_OFS, 16, img->campaign);
flash_ext_write_word(&flash_obj, base + OTA_CRC32_OFS, img->crc32);
return 0;
} }
region1.base = OTA_REGION1; uint32_t OTA_UpdateImage(uint32_t base, uint32_t offset, uint32_t len, uint8_t *data)
region2.base = OTA_REGION2;
OTA_GetImageInfo(&region1);
OTA_GetImageInfo(&region2);
if (region1.ver >= region2.ver) {
ota_base = region2.base;
} else {
ota_base = region1.base;
}
return ota_base;
}
uint32_t OTA_MarkUpdateDone(void)
{
uint32_t addr = OTA_GetBase() + TAG_OFS;
return flash_ext_write_word(&flash_obj, addr, TAG_DOWNLOAD);
}
uint32_t OTA_UpdateImage(uint32_t offset, uint32_t len, uint8_t *data)
{ {
uint32_t addr, start, end, count, shift; uint32_t addr, start, end, count, shift;
uint8_t *pdata = data; uint8_t *pdata = data;
uint8_t buf[FLASH_SECTOR_SIZE]; uint8_t buf[FLASH_SECTOR_SIZE];
start = OTA_GetBase() + offset; start = base + offset;
end = start + len; end = start + len;
if (data == NULL || start > FLASH_TOP || end > FLASH_TOP) { if (data == NULL ||
base != OTA_REGION1_BASE || base != OTA_REGION2_BASE ||
start > FLASH_TOP || end > FLASH_TOP) {
return 0; return 0;
} }
@ -96,7 +139,6 @@ uint32_t OTA_UpdateImage(uint32_t offset, uint32_t len, uint8_t *data)
} }
while (addr < end) { while (addr < end) {
printf("OTA: update addr=0x%lx, len=%ld\r\n", addr, len);
count = MIN(FLASH_SECTOR_SIZE, end - addr); count = MIN(FLASH_SECTOR_SIZE, end - addr);
flash_ext_erase_sector(&flash_obj, addr); flash_ext_erase_sector(&flash_obj, addr);
flash_ext_stream_write(&flash_obj, addr, count, pdata); flash_ext_stream_write(&flash_obj, addr, count, pdata);
@ -106,19 +148,20 @@ uint32_t OTA_UpdateImage(uint32_t offset, uint32_t len, uint8_t *data)
return len; return len;
} }
uint32_t OTA_ReadImage(uint32_t offset, uint32_t len, uint8_t *data) uint32_t OTA_ReadImage(uint32_t base, uint32_t offset, uint32_t len, uint8_t *data)
{ {
uint32_t addr, endaddr; uint32_t start, end;
addr = OTA_GetBase() + offset; start = base + offset;
endaddr = addr + len; end = start + len;
if (data == NULL || addr > FLASH_TOP || endaddr > FLASH_TOP) { if (data == NULL ||
base != OTA_REGION1_BASE || base != OTA_REGION2_BASE ||
start > FLASH_TOP || end > FLASH_TOP) {
return 0; return 0;
} }
printf("OTA: read addr=0x%lx\r\n", addr); return flash_ext_stream_read(&flash_obj, start, len, data);
return flash_ext_stream_read(&flash_obj, addr, len, data);
} }
void OTA_ResetTarget(void) void OTA_ResetTarget(void)
@ -126,11 +169,7 @@ void OTA_ResetTarget(void)
__RTK_CTRL_WRITE32(0x14, 0x00000021); __RTK_CTRL_WRITE32(0x14, 0x00000021);
wait(1); wait(1);
// write SCB->AIRCR NVIC_SystemReset();
HAL_WRITE32(0xE000ED00, 0x0C,
(0x5FA << 16) | // VECTKEY
(HAL_READ32(0xE000ED00, 0x0C) & (7 << 8)) | // PRIGROUP
(1 << 2)); // SYSRESETREQ
// not reached // not reached
while (1); while (1);

View File

@ -1,33 +1,75 @@
/* mbed Microcontroller Library
* Copyright (c) 2013-2017 Realtek Semiconductor Corp.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_OTA_API_H #ifndef MBED_OTA_API_H
#define MBED_OTA_API_H #define MBED_OTA_API_H
#define FLASH_TOP 0x200000 #define FLASH_TOP 0x200000
#define FLASH_SECTOR_SIZE 0x1000 #define FLASH_SECTOR_SIZE 0x1000
#define FLASH_SECTOR_MASK ~(FLASH_SECTOR_SIZE - 1) #define FLASH_SECTOR_MASK ~(FLASH_SECTOR_SIZE - 1)
#define OTA_REGION1 0x0b000
#define OTA_REGION2 0xc0000
#define TAG_OFS 0xc
#define VER_OFS 0x10
#define TAG_DOWNLOAD 0x81950001 #define OTA_REGION1_BASE 0x40000
#define TAG_VERIFIED 0x81950003 #define OTA_REGION2_BASE 0x120000
#define OTA_REGION1_SIZE 0xe0000
#define OTA_REGION2_SIZE 0xe0000
#define OTA_REGION_SIZE 0xe0000
#define OTA_MBED_FS_BASE 0xb000
#define OTA_CRC32_LEN 0x44
#define OTA_HEADER_LEN 0x48
#define OTA_HEADER_OFS 0x0
#define OTA_TAG_OFS 0x0
#define OTA_VER_OFS 0x4
#define OTA_EPOCH_OFS 0x8
#define OTA_SIZE_OFS 0x10
#define OTA_HASH_OFS 0x14
#define OTA_CAMPAIGN_OFS 0x34
#define OTA_CRC32_OFS 0x44
#define OTA_IMAGE_OFS 0x48
#define OTA_TAG_ID 0x81950001
#define OTA_VER_ID 0x81950001
#define OTA_TAG_CHIP_MSK 0xFFFF0000
#define OTA_TAG_INFO_MSK 0x0000FFFF
typedef struct imginfo_s { typedef struct imginfo_s {
uint32_t base;
uint32_t tag; uint32_t tag;
uint64_t ver; uint32_t ver;
uint64_t timestamp;
uint32_t size;
uint8_t hash[32];
uint8_t campaign[16];
uint32_t crc32;
bool valid;
} imginfo_t; } imginfo_t;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
extern void OTA_GetImageInfo(imginfo_t *info); extern void OTA_GetImageInfo(uint32_t base, imginfo_t *info);
extern uint32_t OTA_GetBase(void); extern uint32_t OTA_GetUpdateBase(void);
extern uint32_t OTA_UpdateImage(uint32_t offset, uint32_t len, uint8_t *data); extern uint32_t OTA_UpdateHeader(uint32_t base, imginfo_t *img);
extern uint32_t OTA_ReadImage(uint32_t offset, uint32_t len, uint8_t *data); extern uint32_t OTA_UpdateImage(uint32_t base, uint32_t offset, uint32_t len, uint8_t *data);
extern uint32_t OTA_MarkUpdateDone(void); extern void OTA_ReadHeader(uint32_t base, imginfo_t *img);
extern uint32_t OTA_ReadImage(uint32_t base, uint32_t offset, uint32_t len, uint8_t *data);
extern void OTA_ResetTarget(void); extern void OTA_ResetTarget(void);
#ifdef __cplusplus #ifdef __cplusplus
@ -35,4 +77,3 @@ extern void OTA_ResetTarget(void);
#endif #endif
#endif /* MBED_OTA_API_H */ #endif /* MBED_OTA_API_H */

View File

@ -7,16 +7,26 @@ RTL8195A elf2bin script
import sys, array, struct, os, re, subprocess import sys, array, struct, os, re, subprocess
import hashlib import hashlib
import shutil import shutil
import time
import binascii
from tools.paths import TOOLS_BOOTLOADERS from tools.paths import TOOLS_BOOTLOADERS
from tools.toolchains import TOOLCHAIN_PATHS from tools.toolchains import TOOLCHAIN_PATHS
from datetime import datetime
# Constant Variables # Constant Variables
RAM2_RSVD = 0x00000000 TAG = 0x81950001
RAM2_VER = 0x8195FFFF00000000 VER = 0x81950001
RAM2_TAG = 0x81950001 CAMPAIGN = binascii.hexlify('FFFFFFFFFFFFFFFF')
RAM2_SHA = '0'
RAM2_HEADER = {
'tag': 0,
'ver': 0,
'timestamp': 0,
'size': 72,
'hash': 'FF',
'campaign': 'FF',
'crc32': 0xFFFFFFFF,
}
def format_number(number, width): def format_number(number, width):
# convert to string # convert to string
@ -53,6 +63,9 @@ def write_padding_bytes(output_name, size):
output.write('\377' * padcount) output.write('\377' * padcount)
output.close() output.close()
def crc32_checksum(string):
return binascii.crc32(string) & 0xFFFFFFFF
def sha256_checksum(filename, block_size=65536): def sha256_checksum(filename, block_size=65536):
sha256 = hashlib.sha256() sha256 = hashlib.sha256()
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
@ -60,42 +73,9 @@ def sha256_checksum(filename, block_size=65536):
sha256.update(block) sha256.update(block)
return sha256.hexdigest() return sha256.hexdigest()
def get_version_by_time(): def epoch_timestamp():
secs = int((datetime.now()-datetime(2016,11,1)).total_seconds()) epoch = int(time.time())
return RAM2_VER + secs return epoch
# ----------------------------
# main function
# ----------------------------
def prepend(image, entry, segment, image_ram2, image_ota):
# parse input arguments
output = open(image_ram2, "wb")
write_number(os.stat(image).st_size, 8, output)
write_number(int(entry), 8, output)
write_number(int(segment), 8, output)
RAM2_SHA = sha256_checksum(image)
write_number(RAM2_TAG, 8, output)
write_number(get_version_by_time(), 16, output)
write_string(RAM2_SHA, 64, output)
write_number(RAM2_RSVD, 8, output)
append_image_file(image, output)
output.close()
ota = open(image_ota, "wb")
write_number(os.stat(image).st_size, 8, ota)
write_number(int(entry), 8, ota)
write_number(int(segment), 8, ota)
write_number(0xFFFFFFFF, 8, ota)
write_number(get_version_by_time(), 16, ota)
write_string(RAM2_SHA, 64, ota)
write_number(RAM2_RSVD, 8, ota)
append_image_file(image, ota)
ota.close()
def find_symbol(toolchain, mapfile, symbol): def find_symbol(toolchain, mapfile, symbol):
ret = None ret = None
@ -235,9 +215,15 @@ def parse_load_segment(toolchain, image_elf):
else: else:
return [] return []
def write_load_segment(image_elf, image_bin, segment): def create_payload(image_elf, ram2_bin, entry, segment):
file_elf = open(image_elf, "rb") file_elf = open(image_elf, "rb")
file_bin = open(image_bin, "wb") file_bin = open(ram2_bin, "wb")
write_number(int(entry), 8, file_bin)
write_number(int(len(segment)), 8, file_bin)
write_number(0xFFFFFFFF, 8, file_bin)
write_number(0xFFFFFFFF, 8, file_bin)
for (offset, addr, size) in segment: for (offset, addr, size) in segment:
file_elf.seek(offset) file_elf.seek(offset)
# write image header - size & addr # write image header - size & addr
@ -252,30 +238,48 @@ def write_load_segment(image_elf, image_bin, segment):
file_bin.close() file_bin.close()
file_elf.close() file_elf.close()
# ---------------------------- def create_daplink(image_bin, ram1_bin, ram2_bin):
# main function
# ----------------------------
def rtl8195a_elf2bin(t_self, image_elf, image_bin):
# remove target binary file/path # remove target binary file/path
if os.path.isfile(image_bin): if os.path.isfile(image_bin):
os.remove(image_bin) os.remove(image_bin)
else: else:
shutil.rmtree(image_bin) shutil.rmtree(image_bin)
segment = parse_load_segment(t_self.name, image_elf) RAM2_HEADER['tag'] = format_number(TAG, 8)
write_load_segment(image_elf, image_bin, segment) RAM2_HEADER['ver'] = format_number(VER, 8)
RAM2_HEADER['timestamp'] = format_number(epoch_timestamp(), 16)
RAM2_HEADER['size'] = format_number(os.stat(ram2_bin).st_size + 72, 8)
RAM2_HEADER['hash'] = format_string(sha256_checksum(ram2_bin))
RAM2_HEADER['campaign'] = format_string(CAMPAIGN)
output = open(image_bin, "wb")
append_image_file(ram1_bin, output)
line = ""
for key in ['tag', 'ver', 'timestamp', 'size', 'hash', 'campaign']:
line += RAM2_HEADER[key]
output.write(RAM2_HEADER[key])
RAM2_HEADER['crc32'] = format_number(crc32_checksum(line), 8)
output.write(RAM2_HEADER['crc32'])
append_image_file(ram2_bin, output)
output.close()
# ----------------------------
# main function
# ----------------------------
def rtl8195a_elf2bin(t_self, image_elf, image_bin):
image_name = os.path.splitext(image_elf)[0] image_name = os.path.splitext(image_elf)[0]
image_map = image_name + '.map' image_map = image_name + '.map'
ram2_ent = find_symbol(t_self.name, image_map, "PLAT_Start")
ram1_bin = os.path.join(TOOLS_BOOTLOADERS, "REALTEK_RTL8195AM", "ram_1.bin") ram1_bin = os.path.join(TOOLS_BOOTLOADERS, "REALTEK_RTL8195AM", "ram_1.bin")
ram2_bin = image_name + '-ram_2.bin' ram2_bin = image_name + '-payload.bin'
ota_bin = image_name + '-ota.bin'
prepend(image_bin, ram2_ent, len(segment), ram2_bin, ota_bin)
# write output file entry = find_symbol(t_self.name, image_map, "PLAT_Start")
output = open(image_bin, "wb") segment = parse_load_segment(t_self.name, image_elf)
append_image_file(ram1_bin, output)
append_image_file(ram2_bin, output) create_payload(image_elf, ram2_bin, entry, segment)
output.close() create_daplink(image_bin, ram1_bin, ram2_bin)