458 lines
8.2 KiB
C
458 lines
8.2 KiB
C
/*
|
|
* SerialICE
|
|
*
|
|
* Copyright (C) 2009 coresystems GmbH
|
|
* Copyright (C) 2016 Patrick Rudolph <siro@das-labor.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <console/console.h>
|
|
#include <console/serialice.h>
|
|
#include <arch/io.h>
|
|
#include <debug-com.h>
|
|
#include "serialice_priv.h"
|
|
|
|
#define VERSION "1.6"
|
|
|
|
/* Uart wrapper functions */
|
|
|
|
static void sio_flush(void)
|
|
{
|
|
debug_tx_flush();
|
|
}
|
|
|
|
static void sio_putc(u8 byte)
|
|
{
|
|
debug_tx_byte(byte);
|
|
}
|
|
|
|
static u8 sio_getc(void)
|
|
{
|
|
u8 val = 0;
|
|
|
|
val = debug_rx_byte();
|
|
if (val) {
|
|
return val;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* String functions */
|
|
|
|
static void sio_putstring(const char *string)
|
|
{
|
|
/* Very simple, no %d, %x etc. */
|
|
while (*string) {
|
|
if (*string == '\n')
|
|
sio_putc('\r');
|
|
sio_putc(*string);
|
|
string++;
|
|
}
|
|
sio_flush();
|
|
}
|
|
|
|
static void sio_put8(u8 data)
|
|
{
|
|
u8 c;
|
|
|
|
c = (data >> 4) & 0xf;
|
|
sio_put_nibble(c);
|
|
|
|
c = data & 0xf;
|
|
sio_put_nibble(c);
|
|
sio_flush();
|
|
}
|
|
|
|
static void sio_put16(u16 data)
|
|
{
|
|
int i;
|
|
u8 c;
|
|
|
|
for (i = 12; i >= 0; i -= 4) {
|
|
c = (data >> i) & 0xf;
|
|
sio_put_nibble(c);
|
|
}
|
|
sio_flush();
|
|
}
|
|
|
|
static void sio_put32(u32 data)
|
|
{
|
|
int i;
|
|
u8 c;
|
|
|
|
for (i = 28; i >= 0; i -= 4) {
|
|
c = (data >> i) & 0xf;
|
|
sio_put_nibble(c);
|
|
}
|
|
sio_flush();
|
|
}
|
|
|
|
static u8 sio_get_nibble(char nibble)
|
|
{
|
|
u8 ret = 0;
|
|
|
|
if (nibble >= '0' && nibble <= '9') {
|
|
ret = (nibble - '0');
|
|
} else if (nibble >= 'a' && nibble <= 'f') {
|
|
ret = (nibble - 'a') + 0xa;
|
|
} else if (nibble >= 'A' && nibble <= 'F') {
|
|
ret = (nibble - 'A') + 0xa;
|
|
} else {
|
|
sio_putstring("ERROR: parsing number\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* caller must ensure 2 bytes given */
|
|
static u8 sio_get8(char *buffer)
|
|
{
|
|
u8 data;
|
|
|
|
data = sio_get_nibble(buffer[0]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[1]);
|
|
return data;
|
|
}
|
|
|
|
/* caller must ensure 4 bytes given */
|
|
static u16 sio_get16(char *buffer)
|
|
{
|
|
u16 data;
|
|
|
|
data = sio_get_nibble(buffer[0]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[1]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[2]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[3]);
|
|
|
|
return data;
|
|
}
|
|
|
|
static u32 sio_get32(char *buffer)
|
|
{
|
|
u32 data;
|
|
|
|
data = sio_get_nibble(buffer[0]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[1]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[2]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[3]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[4]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[5]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[6]);
|
|
data = data << 4;
|
|
data |= sio_get_nibble(buffer[7]);
|
|
|
|
return data;
|
|
}
|
|
|
|
/* SerialICE interface functions */
|
|
|
|
static void serialice_read_memory(char *buffer, int len)
|
|
{
|
|
u8 width;
|
|
u32 *addr;
|
|
|
|
if (len < 13) {
|
|
sio_putstring("Wrong size\n");
|
|
}
|
|
|
|
// Format:
|
|
// *rm00000000.w
|
|
addr = (u32 *)sio_get32(buffer + 3);
|
|
width = buffer[12];
|
|
|
|
switch (width) {
|
|
case 'b':
|
|
sio_put8(read8(addr));
|
|
break;
|
|
case 'w':
|
|
sio_put16(read16(addr));
|
|
break;
|
|
case 'l':
|
|
sio_put32(read32(addr));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void serialice_write_memory(char *buffer, int len)
|
|
{
|
|
u8 width;
|
|
u32 *addr;
|
|
u32 data;
|
|
|
|
// Format:
|
|
// *wm00000000.w=0000
|
|
addr = (u32 *)sio_get32(buffer + 3);
|
|
width = buffer[12];
|
|
|
|
switch (width) {
|
|
case 'b':
|
|
data = sio_get8(buffer + 14);
|
|
write8(addr, (u8)data);
|
|
break;
|
|
case 'w':
|
|
data = sio_get16(buffer + 14);
|
|
write16(addr, (u16)data);
|
|
break;
|
|
case 'l':
|
|
data = sio_get32(buffer + 14);
|
|
write32(addr, (u32)data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void serialice_read_io(char *buffer, int len)
|
|
{
|
|
u8 width;
|
|
u16 port;
|
|
|
|
// Format:
|
|
// *ri0000.w
|
|
port = sio_get16(buffer + 3);
|
|
width = buffer[8];
|
|
|
|
switch (width) {
|
|
case 'b':
|
|
sio_put8(inb(port));
|
|
break;
|
|
case 'w':
|
|
sio_put16(inw(port));
|
|
break;
|
|
case 'l':
|
|
sio_put32(inl(port));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void serialice_write_io(char *buffer, int len)
|
|
{
|
|
u8 width;
|
|
u16 port;
|
|
u32 data;
|
|
|
|
if (len < 14) {
|
|
sio_putstring("failed write_io");
|
|
return;
|
|
}
|
|
|
|
// Format:
|
|
// *wi0000.w=0000
|
|
port = sio_get16(buffer + 3);
|
|
width = buffer[8];
|
|
|
|
switch (width) {
|
|
case 'b':
|
|
data = sio_get8(buffer + 10);
|
|
outb((u8)data, port);
|
|
break;
|
|
case 'w':
|
|
data = sio_get16(buffer + 10);
|
|
outw((u16)data, port);
|
|
break;
|
|
case 'l':
|
|
data = sio_get32(buffer + 10);
|
|
outl((u32)data, port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void serialice_read_msr(char *buffer, int len)
|
|
{
|
|
u32 addr, key;
|
|
msr_t msr;
|
|
|
|
if (len < 20) {
|
|
sio_putstring("read msr to small");
|
|
return;
|
|
}
|
|
|
|
// Format:
|
|
// *rc00000000.9c5a203a
|
|
addr = sio_get32(buffer + 3);
|
|
key = sio_get32(buffer + 12); // key in %edi
|
|
|
|
msr = rdmsr(addr, key);
|
|
sio_put32(msr.hi);
|
|
sio_putc('.');
|
|
sio_put32(msr.lo);
|
|
}
|
|
|
|
static void serialice_write_msr(char *buffer, int len)
|
|
{
|
|
u32 addr, key;
|
|
msr_t msr;
|
|
|
|
if (len < 38) {
|
|
sio_putstring("write msr to small");
|
|
return;
|
|
}
|
|
|
|
// Format:
|
|
// *wc00000000.9c5a203a=00000000.00000000
|
|
addr = sio_get32(buffer + 3);
|
|
key = sio_get32(buffer + 12); // read key in %edi
|
|
msr.hi = sio_get32(buffer + 21);
|
|
msr.lo = sio_get32(buffer + 30);
|
|
|
|
#ifdef __ROMCC__
|
|
/* Cheat to avoid register outage */
|
|
wrmsr(addr, msr, 0x9c5a203a);
|
|
#else
|
|
wrmsr(addr, msr, key);
|
|
#endif
|
|
}
|
|
|
|
static void serialice_cpuinfo(char *buffer, int len)
|
|
{
|
|
u32 eax, ecx;
|
|
u32 reg32;
|
|
|
|
// Format:
|
|
// --EAX--- --ECX---
|
|
// *ci00000000.00000000
|
|
eax = sio_get32(buffer + 3);
|
|
ecx = sio_get32(buffer + 12);
|
|
|
|
/* This code looks quite crappy but this way we don't
|
|
* have to worry about running out of registers if we
|
|
* occupy eax, ebx, ecx, edx at the same time
|
|
*/
|
|
reg32 = s_cpuid_eax(eax, ecx);
|
|
sio_put32(reg32);
|
|
sio_putc('.');
|
|
|
|
reg32 = s_cpuid_ebx(eax, ecx);
|
|
sio_put32(reg32);
|
|
sio_putc('.');
|
|
|
|
reg32 = s_cpuid_ecx(eax, ecx);
|
|
sio_put32(reg32);
|
|
sio_putc('.');
|
|
|
|
reg32 = s_cpuid_edx(eax, ecx);
|
|
sio_put32(reg32);
|
|
}
|
|
|
|
static void serialice_mainboard(char *buffer, int len)
|
|
{
|
|
int i = 0;
|
|
const char mb_string[] = CONFIG_MAINBOARD_VENDOR" "
|
|
CONFIG_MAINBOARD_PART_NUMBER;
|
|
|
|
while (i < 32 && mb_string[i] > 0) {
|
|
sio_putc(mb_string[i]);
|
|
i++;
|
|
}
|
|
while (i < 32) {
|
|
sio_putc(' ');
|
|
i++;
|
|
}
|
|
|
|
sio_flush();
|
|
}
|
|
|
|
static void serialice_version(char *buffer, int len)
|
|
{
|
|
sio_putstring("\nSerialICE v" VERSION "\n");
|
|
}
|
|
|
|
static int sio_get_line(char *buffer, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<len; i++) {
|
|
*buffer = sio_getc();
|
|
if ((*buffer) == '\n')
|
|
return i;
|
|
|
|
buffer++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void serialice_main(void)
|
|
{
|
|
u16 c;
|
|
char line[32];
|
|
int len;
|
|
|
|
serialice_version(line, 0);
|
|
|
|
while (1) {
|
|
sio_putstring("\n> ");
|
|
|
|
len = sio_get_line(line, sizeof(line));
|
|
/* only new line read */
|
|
if (len == 0)
|
|
continue;
|
|
|
|
c = line[0];
|
|
if (c != '*')
|
|
continue;
|
|
|
|
c = line[1] << 8 | line[2];
|
|
|
|
switch (c) {
|
|
case (('r' << 8)|'m'): // Read Memory *rm
|
|
serialice_read_memory(line, len);
|
|
break;
|
|
case (('w' << 8)|'m'): // Write Memory *wm
|
|
serialice_write_memory(line, len);
|
|
break;
|
|
case (('r' << 8)|'i'): // Read IO *ri
|
|
serialice_read_io(line, len);
|
|
break;
|
|
case (('w' << 8)|'i'): // Write IO *wi
|
|
serialice_write_io(line, len);
|
|
break;
|
|
case (('r' << 8)|'c'): // Read CPU MSR *rc
|
|
serialice_read_msr(line, len);
|
|
break;
|
|
case (('w' << 8)|'c'): // Write CPU MSR *wc
|
|
serialice_write_msr(line, len);
|
|
break;
|
|
case (('c' << 8)|'i'): // Read CPUID *ci
|
|
serialice_cpuinfo(line, len);
|
|
break;
|
|
case (('m' << 8)|'b'): // Read mainboard type *mb
|
|
serialice_mainboard(line, len);
|
|
break;
|
|
case (('v' << 8)|'i'): // Read version info *vi
|
|
serialice_version(line, len);
|
|
break;
|
|
case ('?'):
|
|
sio_putstring(""
|
|
"rm - read memory\n"
|
|
"wm - write memory\n"
|
|
"ri - read io\n"
|
|
"wi - write io\n"
|
|
"ci - cpu info\n"
|
|
"mb - read mainboard\n"
|
|
"vi - show version\n");
|
|
break;
|
|
default:
|
|
sio_putstring("ERROR\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|