kolibrios-fun/contrib/other/uarm/SoC.c
maxcodehack a03882245a Upload uARM emulator (linux4kolibri)
git-svn-id: svn://kolibrios.org@8327 a494cfbc-eb01-0410-851d-a64ba20cac60
2020-12-05 14:28:30 +00:00

777 lines
17 KiB
C
Executable File

#include "SoC.h"
#include "CPU.h"
#include "MMU.h"
#include "mem.h"
#include "callout_RAM.h"
#include "RAM.h"
#include "cp15.h"
#include "math64.h"
#include "pxa255_IC.h"
#include "pxa255_TIMR.h"
#include "pxa255_RTC.h"
#include "pxa255_UART.h"
#include "pxa255_PwrClk.h"
#include "pxa255_GPIO.h"
#include "pxa255_DMA.h"
#include "pxa255_DSP.h"
#include "pxa255_LCD.h"
#ifdef EMBEDDED
#include <avr/io.h>
#endif
#define ERR(s) do{err_str(s " Halting\r\n"); while(1); }while(0)
static const UInt8 embedded_boot[] = {
0x01, 0x00, 0x8F, 0xE2, 0x10, 0xFF, 0x2F, 0xE1, 0x04, 0x27, 0x01, 0x20, 0x00, 0x21, 0x00, 0xF0,
0x0D, 0xF8, 0x0A, 0x24, 0x24, 0x07, 0x65, 0x1C, 0x05, 0x27, 0x00, 0x22, 0x00, 0xF0, 0x06, 0xF8,
0x20, 0x60, 0x24, 0x1D, 0x49, 0x1C, 0x80, 0x29, 0xF8, 0xD1, 0x28, 0x47, 0xBC, 0x46, 0xBB, 0xBB,
0x70, 0x47
};
#define ROM_BASE 0x00000000UL
#define ROM_SIZE sizeof(embedded_boot)
#define RAM_BASE 0xA0000000UL
#define RAM_SIZE 0x01000000UL //16M @ 0xA0000000
static Boolean vMemF(ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 size, Boolean write, Boolean priviledged, UInt8* fsrP){
SoC* soc = cpu->userData;
UInt32 pa;
if(size & (size - 1)){ //size is not a power of two
return false;
}
if(vaddr & (size - 1)){ //bad alignment
return false;
}
return mmuTranslate(&soc->mmu, vaddr, priviledged, write, &pa, fsrP) && memAccess(&soc->mem, pa, size, write, buf);
}
static Boolean hyperF(ArmCpu* cpu){ //return true if handled
SoC* soc = cpu->userData;
if(cpu->regs[12] == 0){
err_str("Hypercall 0 caught\r\n");
soc->go = false;
}
else if(cpu->regs[12] == 1){
err_dec(cpu->regs[0]);
}
else if(cpu->regs[12] == 2){
char x[2];
x[1] = 0;
x[0] = cpu->regs[0];
err_str(x);
}
else if(cpu->regs[12] == 3){ //get ram size
cpu->regs[0] = RAM_SIZE;
}
else if(cpu->regs[12] == 4){ //block device access perform [do a read or write]
//IN:
// R0 = op
// R1 = sector
return soc->blkF(soc->blkD, cpu->regs[1], soc->blkDevBuf, cpu->regs[0]);
}
else if(cpu->regs[12] == 5){ //block device buffer access [read or fill emulator's buffer]
//IN:
// R0 = word value
// R1 = word offset (0, 1, 2...)
// R2 = op (1 = write, 0 = read)
//OUT:
// R0 = word value
if(cpu->regs[1] >= BLK_DEV_BLK_SZ / sizeof(UInt32)) return false; //invalid request
if(cpu->regs[2] == 0){
cpu->regs[0] = soc->blkDevBuf[cpu->regs[1]];
}
else if(cpu->regs[2] == 1){
soc->blkDevBuf[cpu->regs[1]] = cpu->regs[0];
}
else return false;
}
return true;
}
static void setFaultAdrF(ArmCpu* cpu, UInt32 adr, UInt8 faultStatus){
SoC* soc = cpu->userData;
cp15SetFaultStatus(&soc->cp15, adr, faultStatus);
}
static void emulErrF(_UNUSED_ ArmCpu* cpu, const char* str){
err_str("Emulation error: <<");
err_str(str);
err_str(">> halting\r\n");
while(1);
}
static Boolean pMemReadF(void* userData, UInt32* buf, UInt32 pa){ //for DMA engine and MMU pagetable walks
ArmMem* mem = userData;
return memAccess(mem, pa, 4, false, buf);
}
static void dumpCpuState(ArmCpu* cpu, char* label){
UInt8 i;
if(label){
err_str("CPU ");
err_str(label);
err_str("\r\n");
}
for(i = 0; i < 16; i++){
err_str("R");
err_dec(i);
err_str("\t= 0x");
err_hex(cpuGetRegExternal(cpu, i));
err_str("\r\n");
}
err_str("CPSR\t= 0x");
err_hex(cpuGetRegExternal(cpu, ARM_REG_NUM_CPSR));
err_str("\r\nSPSR\t= 0x");
err_hex(cpuGetRegExternal(cpu, ARM_REG_NUM_SPSR));
err_str("\r\n");
}
static UInt16 socUartPrvRead(void* userData){ //these are special funcs since they always get their own userData - the uart :)
SoC* soc = userData;
UInt16 v;
int r;
r = soc->rcF();
if(r == CHAR_CTL_C) v = UART_CHAR_BREAK;
else if(r == CHAR_NONE) v = UART_CHAR_NONE;
else if(r >= 0x100) v = UART_CHAR_NONE; //we canot send this char!!!
else v = r;
return v;
}
static void socUartPrvWrite(UInt16 chr, void* userData){ //these are special funcs since they always get their own userData - the uart :)
SoC* soc = userData;
if(chr == UART_CHAR_NONE) return;
soc->wcF(chr);
}
void LinkError_SIZEOF_STRUCT_SOC_wrong();
void socRamModeAlloc(SoC* soc, _UNUSED_ void* ignored){
UInt32* ramB = emu_alloc(RAM_SIZE);
if(!ramB) ERR("Cannot allocate RAM buffer");
if(!ramInit(&soc->ram.RAM, &soc->mem, RAM_BASE, RAM_SIZE, ramB)) ERR("Cannot init RAM");
soc->calloutMem = false;
}
void socRamModeCallout(SoC* soc, void* callout){
if(!coRamInit(&soc->ram.coRAM, &soc->mem, RAM_BASE, RAM_SIZE, callout)) ERR("Cannot init coRAM");
soc->calloutMem = true;
}
#define ERR_(s) ERR("error");
void socInit(SoC* soc, SocRamAddF raF, void*raD, readcharF rc, writecharF wc, blockOp blkF, void* blkD){
printf ("SoC init! \n");
Err e;
soc->rcF = rc;
soc->wcF = wc;
soc->blkF = blkF;
soc->blkD = blkD;
soc->go = true;
e = cpuInit(&soc->cpu, ROM_BASE, vMemF, emulErrF, hyperF, &setFaultAdrF);
if(e){
err_str("Failed to init CPU: ");
// err_dec(e);
// err_str(". Halting\r\n");
while(1);
}
printf("CPU init\n");
soc->cpu.userData = soc;
memInit(&soc->mem);
mmuInit(&soc->mmu, pMemReadF, &soc->mem);
printf("Init complete\n");
if(ROM_SIZE > sizeof(soc->romMem)) {
// err_str("Failed to init CPU: ");
err_str("ROM_SIZE to small");
// err_str(". Halting\r\n");
while(1);
}
printf("RAF\n");
raF(soc, raD);
if(!ramInit(&soc->ROM, &soc->mem, ROM_BASE, ROM_SIZE, soc->romMem)) ERR_("Cannot init ROM");
cp15Init(&soc->cp15, &soc->cpu, &soc->mmu);
__mem_copy(soc->romMem, embedded_boot, sizeof(embedded_boot));
printf("Things...\n");
if(!pxa255icInit(&soc->ic, &soc->cpu, &soc->mem)) ERR_("Cannot init PXA255's interrupt controller");
if(!pxa255timrInit(&soc->timr, &soc->mem, &soc->ic)) ERR_("Cannot init PXA255's OS timers");
if(!pxa255rtcInit(&soc->rtc, &soc->mem, &soc->ic)) ERR_("Cannot init PXA255's RTC");
if(!pxa255uartInit(&soc->ffuart, &soc->mem, &soc->ic,PXA255_FFUART_BASE, PXA255_I_FFUART)) ERR_("Cannot init PXA255's FFUART");
if(!pxa255uartInit(&soc->btuart, &soc->mem, &soc->ic,PXA255_BTUART_BASE, PXA255_I_BTUART)) ERR_("Cannot init PXA255's BTUART");
if(!pxa255uartInit(&soc->stuart, &soc->mem, &soc->ic,PXA255_STUART_BASE, PXA255_I_STUART)) ERR_("Cannot init PXA255's STUART");
if(!pxa255pwrClkInit(&soc->pwrClk, &soc->cpu, &soc->mem)) ERR_("Cannot init PXA255's Power and Clock manager");
if(!pxa255gpioInit(&soc->gpio, &soc->mem, &soc->ic)) ERR_("Cannot init PXA255's GPIO controller");
if(!pxa255dmaInit(&soc->dma, &soc->mem, &soc->ic)) ERR_("Cannot init PXA255's DMA controller");
if(!pxa255dspInit(&soc->dsp, &soc->cpu)) ERR_("Cannot init PXA255's cp0 DSP");
if(!pxa255lcdInit(&soc->lcd, &soc->mem, &soc->ic)) ERR_("Cannot init PXA255's LCD controller");
printf("go?\n");
pxa255uartSetFuncs(&soc->ffuart, socUartPrvRead, socUartPrvWrite, soc);
}
void gdbCmdWait(SoC* soc, unsigned gdbPort, int* ss);
void socRun(SoC* soc, UInt32 gdbPort){
printf("go2?\n");
UInt32 prevRtc = 0;
UInt32 cyclesCapt = 0;
UInt32 cycles = 0; //make 64 if you REALLY need it... later
#ifdef GDB_SUPPORT
int ss = 1; //for gdb stub single step
#else
gdbPort = 0; //use the param somehow to quiet GCC
#endif
printf("run !\n");
while(soc->go){
//printf("Soc go...\n");
cycles++;
#ifdef EMBEDDED
if(!(PIND & 0x10)){ //btn down
if(!prevRtc){
do{
prevRtc = gRtc;
}while(prevRtc != gRtc);
cyclesCapt = 0;
}
else{
UInt32 t;
//we only care to go on if the rtc is now different
do{
t = gRtc;
}while(t != gRtc);
if(t != prevRtc){
if(!cyclesCapt){
//this code assumes we're called often enough that the next rtc vals we see is the NEXT second, not the one after or any other such thing
cyclesCapt = cycles;
prevRtc = t;
}
else{
err_dec(cycles - cyclesCapt);
err_str(" Hz\r\n");
cyclesCapt = 0;
prevRtc = 0;
}
}
}
}
#endif
if(!(cycles & 0x00000FUL)) pxa255timrTick(&soc->timr);
if(!(cycles & 0x0000FFUL)) pxa255uartProcess(&soc->ffuart);
if(!(cycles & 0x000FFFUL)) pxa255rtcUpdate(&soc->rtc);
if(!(cycles & 0x01FFFFUL)) pxa255lcdFrame(&soc->lcd);
#ifdef GDB_SUPPORT
gdbCmdWait(soc, gdbPort, &ss);
#endif
cpuCycle(&soc->cpu);
}
}
#ifdef GDB_SUPPORT
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
static int socdBkptDel(SoC* soc, UInt32 addr, UInt8 sz){
UInt8 i;
for(i = 0; i < soc->nBkpt; i++){
if(soc->bkpt[i] == addr){
soc->nBkpt--;
soc->bkpt[i] = soc->bkpt[soc->nBkpt];
i--;
}
}
return 1;
}
static int socdBkptAdd(SoC* soc, UInt32 addr, UInt8 sz){ //boolean
socdBkptDel(soc, addr, sz);
if(soc->nBkpt == MAX_BKPT) return 0;
soc->bkpt[soc->nBkpt++] = addr;
return 1;
}
static int socdWtpDel(SoC* soc, UInt32 addr, UInt8 sz){
UInt8 i;
for(i = 0; i < soc->nWtp; i++){
if(soc->wtpA[i] == addr && soc->wtpS[i] == sz){
soc->nWtp--;
soc->wtpA[i] = soc->wtpA[soc->nWtp];
soc->wtpS[i] = soc->wtpS[soc->nWtp];
i--;
}
}
return 1;
}
static int socdWtpAdd(SoC* soc, UInt32 addr, UInt8 sz){ //boolean
socdWtpDel(soc, addr, sz);
if(soc->nWtp == MAX_WTP) return 0;
soc->wtpA[soc->nWtp] = addr;
soc->wtpS[soc->nWtp] = sz;
soc->nWtp++;
return 1;
}
UInt32 htoi(const char** cP){
UInt32 i = 0;
const char* in = *cP;
char c;
while((c = *in) != 0){
if(c >= '0' && c <= '9') i = (i * 16) + (c - '0');
else if(c >= 'a' && c <= 'f') i = (i * 16) + (c + 10 - 'a');
else if(c >= 'A' && c <= 'F') i = (i * 16) + (c + 10 - 'A');
else break;
in++;
}
*cP = in;
return i;
}
static UInt32 swap32(UInt32 x){
return ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8) | ((x & 0xff) << 24);
}
int gdb_memAccess(SoC* soc, UInt32 addr, UInt8* buf, int write){
UInt32 pa = 0;
UInt8 fsr = 0;
return mmuTranslate(&soc->mmu, addr, true, false, &pa, &fsr) && memAccess(&soc->mem, pa, 1, write | 0x80, buf);
}
static int addRegToStr(SoC* soc, char* str, int reg){
if(reg == 0x19 || reg < 0x10){
if(reg == 0x19) reg = ARM_REG_NUM_CPSR;
sprintf(str + strlen(str), "%08x", swap32(cpuGetRegExternal(&soc->cpu, reg)));
}
else if(reg >= 0x10 && reg < 0x18){
strcat(str, "000000000000000000000000");
}
else if(reg == 0x18){ //fps
strcat(str, "00000000");
}
else return 0;
return 1;
}
static int interpPacket(SoC* soc,const char* in, char* out, int* ss){ //return 0 if we failed to interp a command, 1 is all ok, -1 to send no reply and run
ArmCpu* cpu = &soc->cpu;
unsigned char c;
unsigned addr, len;
unsigned char* ptr;
int i;
int ret = 1;
if(strcmp(in, "qSupported") == 0){
strcpy(out, "PacketSize=99");
}
else if(strcmp(in, "vCont?") == 0){
out[0] = 0;
}
else if(strcmp(in, "s") == 0){ //single step
*ss = 1;
return -1;
}
else if(strcmp(in, "c") == 0 || in[0] == 'C'){ //continue [with signal, which we ignore]
return -1;
}
else if(in[0] == 'Z' || in[0] == 'z'){
char op = in[0];
char type = in[1];
int (*f)(SoC* soc, UInt32 addr, UInt8 sz) = NULL;
in += 3;
addr = htoi(&in);
if(*in++ != ',') goto fail; //no comma?
len = htoi(&in);
if(*in) goto fail; //string not over?
if(type == '0' || type == '1'){ //bkpt
f = (op == 'Z') ? socdBkptAdd : socdBkptDel;
}
/*
else if(type == '2' || type == '3'){ //wtp
f = (op == 'Z') ? socdWtpAdd : socdWtpDel;
}
else goto fail;
*/
strcpy(out,f(soc, addr, len) ? "OK" : "e00");
}
else if(in[0] == 'H' && (in[1] == 'c' || in[1] == 'g')){
strcpy(out, "OK");
}
else if(in[0] == 'q'){
if(in[1] == 'C'){
strcpy(out, "");
}
else if(strcmp(in +1, "Offsets") == 0){
strcpy(out, "Text=0;Data=0;Bss=0");
}
else goto fail;
}
else if(in[0] == 'p'){ //read register
in++;
i = htoi(&in);
if(*in) goto fail; //string not over?
out[0] = 0;
if(!addRegToStr(soc, out, i)) goto fail;
}
else if(strcmp(in, "g") == 0){ //read all registers
out[0] = 0;
for(i = 0; i < 0x1a; i++) if(!addRegToStr(soc, out, i)) goto fail;
}
else if(in[0] == 'P'){ //write register
in++;
i = htoi(&in);
if(*in++ != '=') goto fail; //string not over?
if(i == 0x19 || i <16){
if(i == 0x19) i = ARM_REG_NUM_CPSR;
addr = htoi(&in);
sprintf(out, "OK");
cpuSetReg(cpu, i, addr);
}
else strcpy(out,"e00");
}
else if(in[0] == 'm'){ //read memory
in++;
addr = htoi(&in);
if(*in++ != ',') goto fail;
len = htoi(&in);
if(*in) goto fail;
out[0] = 0;
while(len--){
if(!gdb_memAccess(soc, addr++, &c, false)) break;
sprintf(out + strlen(out), "%02x", c);
}
}
else if(strcmp(in, "?") == 0){
strcpy(out,"S05");
}
else goto fail;
send_pkt:
return ret;
fail:
out[0] = 0;
ret = 0;
goto send_pkt;
}
static void sendpacket(int sock, char* packet, int withAck){
unsigned int c;
int i;
c = 0;
for(i = 0; i < strlen(packet); i++) c += packet[i];
memmove(packet + (withAck ? 2 : 1), packet, strlen(packet) + 1);
if(withAck){
packet[0] = '+';
packet[1] = '$';
}
else{
packet[0] = '$';
}
sprintf(packet + strlen(packet), "#%02x", c & 0xFF);
//printf("sending packet <<%s>>\n", packet);
send(sock, packet, strlen(packet), 0);
}
void gdbCmdWait(SoC* soc, unsigned gdbPort, int* ss){
ArmCpu* cpu = &soc->cpu;
static int running = 0;
static int sock = -1;
char packet[4096];
struct timeval tv = {0};
fd_set set;
int ret;
if(*ss && running){
strcpy(packet,"S05");
sendpacket(sock, packet, 0);
running = 0; //perform single step
}
*ss = 0;
if(running){ //check for breakpoints
UInt8 i;
for(i = 0; i < soc->nBkpt; i++){
if(soc->cpu.regs[15] == soc->bkpt[i]){
// printf("bkpt hit: pc=0x%08lX bk=0x%08lX i=%d\n", soc->cpu.regs[15], soc->bkpt[i], i);
strcpy(packet,"S05");
sendpacket(sock, packet, 0);
running = 0; //perform breakpoint hit
break;
}
}
}
if(gdbPort){
if(sock == -1){ //no socket yet - make one
struct sockaddr_in sa = {AF_INET, htons(gdbPort)};
socklen_t sl = sizeof(sa);
inet_aton("127.0.0.1", &sa.sin_addr.s_addr);
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1){
err_str("gdb socket creation fails: ");
err_dec(errno);
ERR("\n");
}
ret = bind(sock, (struct sockaddr*)&sa, sizeof(sa));
if(ret){
err_str("gdb socket bind fails: ");
err_dec(errno);
ERR("\n");
}
ret = listen(sock, 1);
if(ret){
err_str("gdb socket listen fails: ");
err_dec(errno);
ERR("\n");
}
ret = accept(sock, (struct sockaddr*)&sa, &sl);
if(ret == -1){
err_str("gdb socket accept fails: ");
err_dec(errno);
ERR("\n");
}
close(sock);
sock = ret;
soc->nBkpt = 0;
soc->nWtp = 0;
}
}
if(gdbPort){
do{
FD_ZERO(&set);
FD_SET(sock, &set);
tv.tv_sec = running ? 0 : 0x00f00000UL;
do{
ret = select(sock + 1, &set, NULL, NULL, &tv);
}while(!ret && !running);
if(ret < 0){
err_str("select fails: ");
err_dec(errno);
ERR("\n");
}
if(ret > 0){
char c;
char* p;
int i, len = 0, esc = 0, end = 0;
ret = recv(sock, &c, 1, 0);
if(ret != 1) ERR("failed to receive byte (1)\n");
if(c == 3){
strcpy(packet,"S11");
sendpacket(sock, packet, 0);
running = 0; //perform breakpoint hit
}
else if(c != '$'){
//printf("unknown packet header '%c'\n", c);
}
else{
do{
if(esc){
c = c ^ 0x20;
esc = 0;
}
else if(c == 0x7d){
esc = 1;
}
if(!esc){ //we cannot be here if we're being escaped
packet[len++] = c;
if(end == 0 && c == '#') end = 2;
else if(end){
end--;
if(!end) break;
}
ret = recv(sock, &c, 1, 0);
if(ret != 1) ERR("failed to receive byte (2)\n");
}
}while(1);
packet[len] = 0;
memmove(packet, packet + 1, len);
len -= 4;
packet[len] = 0;
ret = interpPacket(soc, p = strdup(packet), packet, ss);
if(ret == 0) printf("how do i respond to packet <<%s>>\n", p);
if(ret == -1){ //ack it anyways
char c = '+';
send(sock, &c, 1, 0);
running = 1;
}
else sendpacket(sock, packet, 1);
emu_free(p);
}
}
}while(!running);
}
}
#endif