No mundo dos produtos eletrônicos sabe-se que o termo “segurança” deixou há muito tempo de servir apenas de ferramenta de marketing, para tornar-se uma característica essencial na maioria dos dispositivos desenvolvidos com o mínimo de seriedade pelos seus projetistas. Normas reguladoras de produtos estreitam ainda mais essas condições, garantindo que novos produtos sejam lançados no mercado dentro de um limite de segurança aceitável para o consumidor. Neste artigo trataremos sobre um método on running (Scan Memory) para checar a integridade da memória Flash do PIC18F47K40, eliminando, assim, qualquer possibilidade de um sistema corrompido se manter indevidamente operante.
Security Features: Arquitetura Orientada à Segurança
A varredura do conteúdo de todos os endereços da memória Flash de um microcontrolador pode ser uma tarefa um tanto dispendiosa para aplicações em tempo real. Nem todos os microcontroladores são dotados de recursos suficientemente ágeis, para poder realizar tal procedimento dentro de uma performance aceitável. Geralmente o Memory Scan de um microcontrolador é realizado através de técnicas de DMA, necessitando, para isso, uma arquitetura diferenciada, onde um bloco periférico interno do microcontrolador tem acesso ao conteúdo da memória principal sem a participação ativa da CPU. O Memory Scan se enquadra nas chamadas “Security Features”, funcionalidades especialmente desenvolvidas pelos fabricantes de uC’s para contemplar projetos com níveis de segurança mais altos.
O CRC (Cyclic Redundancy Check)
Vídeo 1: Cálculo manual do CRC por um polinômio gerador de 5° Grau.
O CRC é um método matemático que viabiliza o teste da integridade de uma certa cadeia de dados. Basicamente a operação consiste na divisão polinomial binária da cadeia de dados em teste por um polinômio (binário) gerador, sendo o resto da divisão o resultado da operação propriamente dita. Em processos de comunicação o tal “resto” é anexado ao final da mensagem a ser transmitida. O receptor do outro lado, portando o polinômio gerador, realiza a mesma operação sobre a mensagem recebida (incluindo o CRC anexado). Caso o resultado seja nulo, entende-se que a mensagem foi transmitida sem erros.
Slide 1: Demonstração de uma Importante Propriedade do CRC.
O CRC, além de ser muito poderoso na detecção de erros em processos digitais, mostra-se ainda muito leve em implementação via hardware. Isso se deve à natureza das operações binárias realizadas por esse algoritmo. O leitor que estiver familiarizado com álgebra booleana e circuitos sequenciais poderá entender que o circuito da Figura 1, composto de shift register’s e portas XOR’s, realiza o cálculo do CRC utilizando pouquíssimos recursos de hardware. Tendo em vista tamanha elegância, a implementação em software desse algoritmo acaba ficando menos interessante.
CRC no Scan Memory do PIC 18F47K40
Principais características do Módulo CRC do PIC:
- Implementação em hardware via Linear Feedback Shift Register (Figura 1);
- Configuração do Polinômio Gerador via software (exceto MSB e LSB, que são fixados por hardware como ‘1’);
- Configuração da ordem dos bits via software (big-endian/little-endian);
- Escolha do valor inicial do acumulador do CRC via software (seed value).
Principais características do Módulo Scan Memory do PIC:
- Configuração do range da memória em teste;
- Trabalha com interrupções;
- 4 Modos de checagem da memória:
- Burst Mode: Paralisa a Operação da CPU para a realização da checagem de toda a memória especificada. (Alto throughput para o processo de scan, com o custo de travar o fluxo do programa principal);
- Concurrent Mode: De forma análoga ao Burst Mode, também trava o fluxo do programa principal para a execução do scan, contudo permite a CPU retomar suas atividades nos intervalos dos acessos à memória (access cycles);
- Triggered Mode: Análogo ao Concurrent Mode, contudo o início do Scan Memory se dá por uma fonte de Trigger externa, invés da chamada de uma instrução pela CPU;
- Peek Mode: Opondo-se ao Burst Mode, no Peek Mode o scan é feito sempre nos intervalos que a CPU não tem acesso à memória não-volátil do micro. (Menor throughput para o processo de scan, com a vantagem de não travar o fluxo do programa principal).
Utilizando o Scan Memory do PIC
Este exemplo foi desenvolvido utilizando-se:
- Linguagem: C
- Compilador: xc8
- IDE: MplabX
- Calculadora de CRC oferecida pela Microchip
- Target: PIC18F47K40
- Ambiente: Windows
A verificação da memória de programa “on running” pode ser implementada através das seguintes etapas:
1°) Codificação da Aplicação: nessa etapa o programador deve implementar o código propriamente dito. Um detalhe importante é a declaração de uma variável const que armazenará o CRC da memória de programa em um local específico da própria memória flash.
//O CRC da Memória de Programa será armazenado na última posição da memória //Flash (como ainda não sabemos o valor do CRC, carregamos inicialmente um //valor nulo). const int CRC_checkValue @ 0x1FFFE=0x0000;
2°) Criação de um .txt com a cadeia de opcodes que formam o programa: após compilar o código fonte, acesse os opcodes gerados para a memória Flash através do seguinte caminho:
Window >> PIC Memory Views >> Program Memory
Selecione toda a coluna referente aos opcodes e dê um “Ctrl+C”.
Em um editor de texto cole a coluna dos opcodes. Vale lembrar que o último opcode (0x0000) deve ser apagado, pois essa posição de memória armazenará o CRC que iremos calcular. Salve o documento com um nome qualquer.
3°) Cálculo do CRC: possuindo os opcodes sequenciados em um arquivo separado, abra a calculadora de CRC disponibilizada no site da Microchip. Importe o .txt gerado no passo anterior e calcule o CRC da memória de programa (as configurações do cálculo do CRC devem ser as mesmas escolhidas no firmware).
4º) Carregue o valor calculado no código fonte: O valor calculado pela aplicação externa será carregado na última posição da memória Flash. Assim, quando o firmware periodicamente checar a memória de programa, os valores devem coincidir, caso contrário a memória estará corrompida.
//O CRC da Memória de Programa será armazenado na última posição da memória //Flash. (agora sabemos o valor do CRC). const int CRC_checkValue @ 0x1FFFE=0x189C;
Segue uma implementação do scan memory no modo burst, com o uso de um polinômio gerador 0x1021/CCITT para o módulo CRC:
/*************************************************CONFIG BITS*************************************************************/ #pragma config FEXTOSC = OFF // External Oscillator mode Selection bits->Oscillator not enabled #pragma config RSTOSC = HFINTOSC_64MHZ // Power-up default value for COSC bits->HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1 // CONFIG1H #pragma config CLKOUTEN = OFF // Clock Out Enable bit->CLKOUT function is disabled #pragma config CSWEN = ON // Clock Switch Enable bit->Writing to NOSC and NDIV is allowed #pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable bit->Fail-Safe Clock Monitor enabled // CONFIG2L #pragma config MCLRE = EXTMCLR // Master Clear Enable bit->If LVP = 0, MCLR pin is MCLR; If LVP = 1, RE3 pin function is MCLR #pragma config PWRTE = OFF // Power-up Timer Enable bit->Power up timer disabled #pragma config LPBOREN = OFF // Low-power BOR enable bit->ULPBOR disabled #pragma config BOREN = SBORDIS // Brown-out Reset Enable bits->Brown-out Reset enabled , SBOREN bit is ignored // CONFIG2H #pragma config BORV = VBOR_2P45 // Brown Out Reset Voltage selection bits->Brown-out Reset Voltage (VBOR) set to 2.45V #pragma config ZCD = OFF // ZCD Disable bit->ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON #pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit->PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle #pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit->Stack full/underflow will cause Reset #pragma config DEBUG = OFF // Debugger Enable bit->Background debugger disabled #pragma config XINST = OFF // Extended Instruction Set Enable bit->Extended Instruction Set and Indexed Addressing Mode disabled // CONFIG3L #pragma config WDTCPS = WDTCPS_31 // WDT Period Select bits->Divider ratio 1:65536; software control of WDTPS #pragma config WDTE = OFF // WDT operating mode->WDT Disabled // CONFIG3H #pragma config WDTCWS = WDTCWS_7 // WDT Window Select bits->window always open (100%); software control; keyed access not required #pragma config WDTCCS = SC // WDT input clock selector->Software Control // CONFIG4L #pragma config WRT0 = OFF // Write Protection Block 0->Block 0 (000800-003FFFh) not write-protected #pragma config WRT1 = OFF // Write Protection Block 1->Block 1 (004000-007FFFh) not write-protected #pragma config WRT2 = OFF // Write Protection Block 2->Block 2 (008000-00BFFFh) not write-protected #pragma config WRT3 = OFF // Write Protection Block 3->Block 3 (00C000-00FFFFh) not write-protected #pragma config WRT4 = OFF // Write Protection Block 3->Block 4 (010000-013FFFh) not write-protected #pragma config WRT5 = OFF // Write Protection Block 3->Block 5 (014000-017FFFh) not write-protected #pragma config WRT6 = OFF // Write Protection Block 3->Block 6 (018000-01BFFFh) not write-protected #pragma config WRT7 = OFF // Write Protection Block 3->Block 7 (01C000-01FFFFh) not write-protected // CONFIG4H #pragma config WRTC = OFF // Configuration Register Write Protection bit->Configuration registers (300000-30000Bh) not write-protected #pragma config WRTB = OFF // Boot Block Write Protection bit->Boot Block (000000-0007FFh) not write-protected #pragma config WRTD = OFF // Data EEPROM Write Protection bit->Data EEPROM not write-protected #pragma config SCANE = ON // Scanner Enable bit->Scanner module is available for use, SCANMD bit can control the module #pragma config LVP = ON // Low Voltage Programming Enable bit->Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored // CONFIG5L #pragma config CP = OFF // UserNVM Program Memory Code Protection bit->UserNVM code protection disabled #pragma config CPD = OFF // DataNVM Memory Code Protection bit->DataNVM code protection disabled // CONFIG6L #pragma config EBTR0 = OFF // Table Read Protection Block 0->Block 0 (000800-003FFFh) not protected from table reads executed in other blocks #pragma config EBTR1 = OFF // Table Read Protection Block 1->Block 1 (004000-007FFFh) not protected from table reads executed in other blocks #pragma config EBTR2 = OFF // Table Read Protection Block 2->Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks #pragma config EBTR3 = OFF // Table Read Protection Block 3->Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks #pragma config EBTR4 = OFF // Table Read Protection Block 4->Block 4 (010000-013FFFh) not protected from table reads executed in other blocks #pragma config EBTR5 = OFF // Table Read Protection Block 5->Block 5 (014000-017FFFh) not protected from table reads executed in other blocks #pragma config EBTR6 = OFF // Table Read Protection Block 6->Block 6 (018000-01BFFFh) not protected from table reads executed in other blocks #pragma config EBTR7 = OFF // Table Read Protection Block 7->Block 7 (01C000-01FFFFh) not protected from table reads executed in other blocks // CONFIG6H #pragma config EBTRB = OFF // Boot Block Table Read Protection bit->Boot Block (000000-0007FFh) not protected from table reads executed in other blocks /***********************************************DEFINING CONST's*****************************************************************/ #define _XTAL_FREQ 64000000 #define HAL /********************************************DEFINING PIN's********************************************/ #define LED PORTDbits.RD7 void config_ios();//PORT's Configuration void config_osc();//Oscillator's Configuration
#include <xc.h>
#ifndef HAL
#include "HAL.h"
#endif
void config_ios()
{
/*******************************************************************************
* This function configs the main pin's registers.
******************************************************************************/
//PORTA Configuration
LATA = 0x00;//Latch's set Low
TRISA = 0xFF;//Tristate sets "In"
ANSELA = 0xFF;//Analog Inputs Disabled
WPUA = 0x00;//Internal Weak Pull-ups Disabled
ODCONA = 0x00;//Open Drain Disabled
//PORTB Configuration
LATB = 0x00;//Latch's set Low
TRISB = 0xFF;//Tristate sets "In"
ANSELB = 0xFF;//Analog Inputs Disabled
WPUB = 0x00;//Internal Weak Pull-ups Disabled
ODCONB = 0x00;//Open Drain Disabled
//PORTC Configuration
LATC = 0x00;//Latch's set Low
TRISC = 0xFF;//Tristate sets "In"
ANSELC = 0xFF;//Analog Inputs Disabled
WPUC = 0x00;//Internal Weak Pull-ups Disabled
ODCONC = 0x00;//Open Drain Disabled
//PORTD Configuration
LATD = 0x00;//Latch's set Low
TRISD = 0xEF;//PIN RD7(LED) -> Out
ANSELD = 0xFF;//Analog Inputs Disabled
WPUD = 0x00;//Internal Weak Pull-ups Disabled
ODCOND = 0x00;//Open Drain Disabled
//PORTE Configuration
LATE = 0x00;//Latch's set Low
TRISE = 0xFF;//Tristate sets "In"
ANSELE = 0xFF;//Analog Inputs Disabled
WPUE = 0x00;//Internal Weak Pull-ups Disabled
ODCONE = 0x00;//Open Drain Disabled
return;
}
void config_osc()
{
/*******************************************************************************
* This function configs the main osc's registers.
******************************************************************************/
// NOSC HFINTOSC; NDIV 1;
OSCCON1 = 0x60;
// CSWHOLD may proceed; SOSCPWR Low power;
OSCCON3 = 0x00;
// MFOEN disabled; LFOEN disabled; ADOEN disabled; SOSCEN disabled; EXTOEN disabled; HFOEN disabled;
OSCEN = 0x00;
// HFFRQ 64_MHz;
OSCFRQ = 0x08;
// TUN 0;
OSCTUNE = 0x00;
return;
}
/*******************************************************************************
Programmer: Caio Cesar Bosco
Date: 28/12/2017
Code: Scan Memory with CRC on PIC18F47K40
* This code do the scan memory in Burst Mode with internal DMA and CRC PIC resources.
* It's just a Microchip Application Note Implementation, for more
* information access:
*
* https://ww1.microchip.com/downloads/en/AppNotes/90003128A.pdf
* https://ww1.microchip.com/downloads/en/AppNotes/00001817A.pdf
*
*
******************************************************************************/
#include <xc.h>
#include "HAL.h"
//******************************************************************************
//The lastest Two address holds Memory's CRC value (computed by a external application).
//Download CRC Calculator on Microchip Site:
// https://www.microchip.com/mymicrochip/filehandler.aspx?ddocname=en572174
const int CRC_checkValue @ 0x1FFFE=0x189C;
//****************************************************************************
void main()
{
int crc=0;//Receives On Running CRC value.
config_osc();//Configure Internal Oscillator on 64MHz
config_ios();//Configure uC pins
SCANCON0bits.EN=1;//Enable Flash Scan_Memory.
SCANCON0bits.MODE=1;//Burst Mode - Stop Firmware Flow for Scanning.
SCANCON0bits.INTM=0;//No interrupts along Scanning Process.
//***********Selecting Initial Flash Memory Address for Scanning************
SCANLADRU=0;
SCANLADRL=0;
SCANLADRH=0;
//**************************************************************************
//***********Selecting Last Flash Memory Address for Scanning************
SCANHADRU=0x01;
SCANHADRL=0xFC;
SCANHADRH=0xFF;
//Microchip PIC18F47K40's an 8 bits architecture, but opcode's a 16 bits
//instruction. So the last valid flash address's: 0x1FFFC(opcode MSB)-0x1FFFD(opcode LSB)
//0x1FFFE - 0x1FFFF holds Flash Memory CRC Value.
//**************************************************************************
CRCCON0bits.EN=1;//Enable Internal CRC Module
CRCACCH=0xFF;//Seeding CRC Accumulator
CRCACCL=0xFF;//Seeding CRC Accumulator
//************************16 bit Polynomial CRC-CCITT*************************
//Polynomial: x^16+x^15+x^5+1
CRCXORH=0x10;
CRCXORL=0x21;
//****************************************************************************
CRCCON1bits.DLEN=0b0111;//Data Lenght
CRCCON1bits.PLEN=0b1111;//Polynomial Lenght
CRCCON0bits.ACCM=1;//Augmented Mode On
CRCCON0bits.SHIFTM=0;//Big-Endian
LED=0;//Led off
while(1)//Super-Loop
{
CRCCON0bits.CRCGO=1;//Run CRC Module
SCANCON0bits.GO=1;//Run Scan Memory
while(SCANCON0bits.GO);//Waiting Scan
while(CRCCON0bits.BUSY);//Waiting CRC Module
crc=CRCACCL+(CRCACCH<<8);//Holds "On Running" CRC value
if(crc==CRC_checkValue)
{
/*******************************************************************
*If "On Running" memory CRC is the same as the Previously Memory CRC Value,
toggles LED.
*******************************************************************/
LED=~LED;
/******************************************************************
(If necessary put a delay routine here).
******************************************************************/
}
}
}
Conclusões
Concluímos que a verificação da memória de programa do PIC pode ser um processo viável a aplicações críticas caso a arquitetura do microcontrolador possuir: acesso direto à memória (DMA – Direct Memory Access) e um módulo de hardware dedicado para o cálculo do CRC. Vimos ainda que existem 4 modos distintos para realizar a checagem da memória, garantindo ao projetista uma significativa liberdade no desenvolvimento do produto.
Espero que o artigo tenha sido útil, qualquer dúvida é só fazer um comentário abaixo.
Saudações e até a próxima!
Saiba mais sobre Segurança em Sistemas Embarcados
O seu dispositivo IoT é seguro?
Webinar Gravado: Segurança em Sistemas Embarcados: Onde, Como e Por quê?
Segurança da Informação – Criptografia AES
Referências
Datasheet do PIC18(L)F27/47K40
Computer Interfacing and Protocols
Using a Hardware or Software CRC with Enhanced Core PIC16F1XXX in Class B Applications







Ainda não testei essa função CRC, no site mercadolivre, tem o PIC18F46K40 (64k Flash), acho que tem quase tudo que tem no PIC18F47K40 (128k Flash), parece que só muda a capacidade de código da flash, será que esse exemplo roda no 46K40 sem nenhuma alteração?
Encontrei uma tabela de modelos de 8-bits com esta funcionalidade de CRC:
PIC12F1612, PIC16F1613, PIC16F1614, PIC16F1615, PIC16F1618, PIC16F1619, PIC16F18854, PIC16F18855, PIC16F18856, PIC16F18857, PIC16F18875, PIC16F18876, PIC16F18877.
PIC18F24K40, PIC18F24K42, PIC18F24Q10, PIC18F25K40, PIC18F25K42, PIC18F25Q10, PIC18F26K40, PIC18F26K42, PIC18F26Q10, PIC18F27K40, PIC18F27K42, PIC18F27Q10, PIC18F45K40, PIC18F45K42, PIC18F45Q10, PIC18F46K40, PIC18F46K42, PIC18F46Q10, PIC18F47K40, PIC18F47K42, PIC18F47Q10, PIC18F55K42, PIC18F56K42, PIC18F57K42, PIC18F65K40, PIC18F66K40, PIC18F67K40.
Fonte: https://www.microchip.com//wwwAppNotes/AppNotes.aspx?appnote=en573120
Se os registradores forem os mesmos acredito que sim. Caso contrário dê uma olhada nas applications notes da Microchip nas referências do artigo. São poucos registradores que necessitam de setup, de quebra você já conheceria mais a fundo cada um deles.
Isso ajuda muito, mas enquanto a fabricante continuar dizendo que o produto deve ser usado por conta e risco do usuário, então a segurança continua duvidosa. Ver datasheet Pg 637: “O uso de dispositivos Microchip em suporte de vida e / ou aplicações de segurança é inteiramente o risco do comprador e o comprador concorda em defender, indenizar e isentar a Microchip de todos e quaisquer danos, reclamações, ações ou despesas decorrentes desse uso.”