Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / devices / mem_bus / libio / io.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: io.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named 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.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)io.c 1.4 07/03/23 SMI"
/*
* Complete the parsing of a generic I/O device.
*
* Pick out the relevent info and allocated the
* simcpu_t structure in the config_cpu that
* gets handed to us.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <strings.h>
#include "basics.h"
#include "allocate.h"
#include "lexer.h"
#include "simcore.h"
#include "config.h"
#include "dumpinfo.h"
#include "strutil.h"
#include "fatal.h"
#include "sparcv9regs.h"
#include "device.h"
#include "bswap.h"
/*
* static only to avoid name clashes with other
* modules ... in reality these are exported
* via the cpu_type_t struct pointers
*/
static void io_parse(config_dev_t *);
static void io_init(config_dev_t *);
static void io_dump(config_dev_t *);
static tpaddr_t io_cacheable(config_addr_t *, dev_access_t type,
tpaddr_t off, uint8_t **cbp);
static bool_t io_cpu_access(simcpu_t *, config_addr_t *, tpaddr_t off,
maccess_t op, uint64_t *regp);
dev_type_t dev_type_io = {
"io",
io_parse,
io_init,
io_dump,
generic_device_non_cacheable,
io_cpu_access,
DEV_MAGIC
};
typedef struct {
uint8_t access_sizes;
bool_t is_le; /* Little Endian Device */
bool_t is_rom; /* mmap file with MAP_PRIVATE flag */
uint8_t *datap;
uint64_t size; /* size of I/O segment */
} io_dev_t;
/*
* Complete the creation and parsing of the io directives
*
* Format for parsing internal io data is:
*
* rom : This is a ROM device i.e no writes allowed
* le : This is a Little Endian device
* access_sizes : Bitmask of valid access sizes for this device
* Bit0 : Byte access ( 8 bit )
* Bit1 : Half word access ( 16 bit )
* Bit2 : Long word access ( 32 bit )
* Bit3 : Xtended word access ( 64 bit )
*/
void
io_parse(config_dev_t *config_devp)
{
lexer_tok_t tok;
io_dev_t *iodp;
uint64_t io_segment_size; /* initial value of iodp->size */
long pgsize;
pgsize = getpagesize();
/*
* Allocate the io device and all that stuff
*/
iodp = Xcalloc(1, io_dev_t);
iodp->access_sizes = 1 + 2 + 4 + 8; /* 1 2 4 or 8 byte accesses */
iodp->is_le = false;
iodp->datap = (void*)0;
iodp->size = config_devp->addrp->range;
/* CSTYLED */
DBG( printf("io_parse: parsing device %d\n", config_devp->device_id); );
tok = lex_get_token();
switch (tok) {
case T_S_Colon:
goto finished; /* nothing more to parse */
case T_L_Brace:
break;
default:
lex_fatal("expecting either ; or { when parsing io device");
}
/*
* Start the parsing loop
*/
do {
char *fnamep;
uint64_t startoffset = 0LL;
uint64_t foffset = 0LL;
uint64_t flen = 0LL;
struct stat sb;
int idx;
tok = lex_get_token();
if (tok == T_R_Brace) break;
if (tok != T_Token) {
}
if (streq(lex.strp, "rom")) {
iodp->is_rom = true;
lex_get(T_S_Colon);
continue;
} else if (streq(lex.strp, "le")) {
iodp->is_le = true;
lex_get(T_S_Colon);
continue;
} else if (streq(lex.strp, "access_sizes")) {
tok = lex_get_token();
if (tok != T_Number) {
printf("tok=%x %d\n", tok, tok);
lex_fatal("Expected a number after access_sizes (e.g 0x1 0x2 0x7 0x8 or 0xf)");
}
iodp->access_sizes = lex.val;
lex_get(T_S_Colon);
continue;
}
tok = lex_get_token();
switch (tok) {
case T_Plus:
lex_get(T_Number);
startoffset = lex.val;
break;
case T_Number:
if (lex.val < config_devp->addrp->baseaddr ||
lex.val >= config_devp->addrp->topaddr)
lex_fatal("specified load address is outside the "
"range of the io device");
startoffset = lex.val - config_devp->addrp->baseaddr;
break;
default:
lex_fatal("Expected either a start address / offset or filename "
"for io device load directive");
}
lex_get(T_S_Colon);
} while (1);
finished:;
config_devp->devp = iodp;
}
void
io_init(config_dev_t *config_devp)
{
long pgsize;
io_dev_t *iodp;
iodp = (io_dev_t *)config_devp->devp;
pgsize = getpagesize();
if (iodp->size < pgsize) {
DBG( fprintf(stderr, "Using realloc %p size %x\n", iodp, iodp->size); );
iodp->datap = Xrealloc(iodp->datap, iodp->size);
return;
}
DBG( fprintf(stderr, "Using mmap %p size %x\n", iodp, iodp->size); );
iodp->datap = iodp->datap = (void*)mmap(NULL, sim_roundup(iodp->size, pgsize),
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0);
if (MAP_FAILED == iodp->datap) fatal("Initial mmap of anon memory failed");
return;
}
/*
* I/O configuration dump
*/
void
io_dump(config_dev_t *config_devp)
{
}
/*
* Should only get invoked if this is a ROM.
* Indicate the store failed - return false
* ROMs are read only.
*/
bool_t
io_cpu_access(simcpu_t *sp, config_addr_t *cap, tpaddr_t offset, maccess_t op, uint64_t *regp)
{
io_dev_t *iodp;
bool_t ret = true;
iodp = cap->config_devp->devp;
if ((offset < 0) || (offset >= cap->range)) {
return false;
}
if (((1 << (op & MA_Size_Mask)) & iodp->access_sizes) == 0) {
EXEC_WARNING(("Access size of %d bytes is not valid for this "
"device [PA=0x%llx, SIZE=0x%llx] @ %%PC 0x%llx "
"offset 0x%llx", (1 << (op & MA_Size_Mask)),
cap->baseaddr, cap->range, sp->pc, offset));
/* return success anyways !!! */
return (false);
}
switch(op & MA_Op_Mask) {
case MA_Ld:
switch(op & MA_Size_Mask) {
case MA_Size8:
*regp = *(uint8_t *)(iodp->datap + offset);
break;
case MA_Size16:
*regp = iodp->is_le ? BSWAP_16(*(uint16_t *)(iodp->datap + offset)) :
*(uint16_t *)(iodp->datap + offset);
break;
case MA_Size32:
*regp = iodp->is_le ? BSWAP_32(*(uint32_t *)(iodp->datap + offset)) :
*(uint32_t *)(iodp->datap + offset);
break;
case MA_Size64:
*regp = iodp->is_le ? BSWAP_64(*(uint64_t *)(iodp->datap + offset)) :
*(uint64_t *)(iodp->datap + offset);
break;
default:
ret = false;
break;
}
break;
case MA_LdSigned:
switch(op & MA_Size_Mask) {
case MA_Size8:
*regp = *(int8_t *)(iodp->datap + offset);
break;
case MA_Size16:
*regp = iodp->is_le ? (sint16_t)BSWAP_16(*(uint16_t *)(iodp->datap + offset)) :
*(sint16_t *)(iodp->datap + offset);
break;
case MA_Size32:
*regp = iodp->is_le ? (sint32_t)BSWAP_32(*(uint32_t *)(iodp->datap + offset)) :
*(sint32_t *)(iodp->datap + offset);
break;
default:
ret = false;
break;
}
break;
case MA_St:
if (iodp->is_rom) {
EXEC_WARNING(("rom_cpu_store: attempted store to ROM @ %%PC 0x%llx "
"of %d bytes at offset 0x%llx",
sp->pc, 1 << (op & MA_Size_Mask), offset));
/* Return success anyways !! */
return (true);
}
switch(op & MA_Size_Mask) {
case MA_Size8:
*((int8_t *)(iodp->datap + offset)) = *regp;
break;
case MA_Size16:
*((int16_t *)(iodp->datap + offset)) = iodp->is_le ? BSWAP_16(*regp) : *regp;
break;
case MA_Size32:
*((int32_t *)(iodp->datap + offset)) = iodp->is_le ? BSWAP_32(*regp) : *regp;
break;
case MA_Size64:
*((int64_t *)(iodp->datap + offset)) = iodp->is_le ? BSWAP_64(*regp) : *regp;
break;
default:
ret = false;
break;
}
break;
default:
ret = false;
break;
}
return ret;
}