Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / parser / parser.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: parser.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 "@(#)parser.c 1.35 07/04/02 SMI"
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <dlfcn.h>
#include <link.h>
#include <string.h>
#include <search.h>
#include "basics.h"
#include "fatal.h"
#include "strutil.h"
#include "allocate.h"
#include "simcore.h"
#include "config.h"
#include "options.h"
#include "fileutil.h"
#include "lexer.h"
#include "sam_dev.h"
#if ERROR_INJECTION
#define CPP_OPTIONS " -DERROR_INJECTION"
#else
#define CPP_OPTIONS ""
#endif
#define MAX_TRIES 4
extern void lex_fatal(char * str, ...);
/*
* Parse the top-level config file.
* Essentially this is restricted to the systems
* directive in the config file
*/
static void parse_proc(domain_t *domainp);
static void parse_device(domain_t *domainp);
static void parse_mmi_device(domain_t *domainp);
static void parse_addressmap(domain_t *domainp);
static void parse_domain(domain_t *domainp);
static void parse_domains(system_t *systemp);
static void parse_systems();
static void parse_service_processor(system_t *systemp);
static void parse_debug();
static void parse_save_state();
void *dlopen_lib(char *mdlnamep, char *symname, char *libname);
proc_type_t * find_proc_type(char * procname);
dev_type_t * find_dev_type(char * devname);
extern void sam_init_device(char *dev_namep, config_dev_t *config_devp);
extern void sam_parse_dev(domain_t *domainp, config_dev_t *config_devp);
extern dev_type_t *sam_load_device(config_dev_t *config_devp);
target_config_t target_config; /* define here ? */
/* list of dev_type_t pointers to dl'd devices */
LIST_DEF(dev_cache, dev_type_t);
/* list of proc_type_t pointers to dl'd processors */
LIST_DEF(proc_cache, proc_type_t);
/*
* Setup empty configuration prior to config parsing
*/
void init_target_config()
{
LIST_INIT(target_config.systems, system_t);
/* Init list of loaded device libraries */
LIST_INIT(dev_cache, dev_type_t);
/* Init list of loaded processor libraries */
LIST_INIT(proc_cache, proc_type_t);
}
/*
* This function calls into the lexer to
* parse the config and state files.
*/
void parse_config_file()
{
char tempfilep[64];
char buffer[8192]; /* big space */
int count;
int res;
FILE * fp;
/*
* First step is to run the C pre-processor
* over the config file.
* Output from the pre-processor, is piped
* directly into the lexer.
* This is complicated because the pre-processor also
* outputs errors, and may fail.
*/
/* FIXME:
* Should probably do this properly with pipes etc.
* but for now, just use a temp file in /tmp
* for the sake of convenience.
*/
for (count = 0; count < MAX_TRIES; count++) {
sprintf(tempfilep, "/tmp/sim.cfg.%d.%02d", (int)getpid(), count);
if (!file_exists(tempfilep)) break;
}
if (count == 5) fatal("Unable to create a temporary file for config pre-processing");
errno = 0;
sprintf(buffer, "%s" CPP_OPTIONS " %s %s > %s",
options.cpp_cmd, options.cpp_optionsp,
options.config_filep, tempfilep);
do {
DBG( printf("system(%s)\n", buffer); );
res = system(buffer);
} while (res == -1 && (errno == EAGAIN || errno == EINTR));
if (res == -1) fatal("Failed trying to pre-process config file %s\n", options.config_filep);
DBG( printf("Exit status %d\n", res); );
fp = fopen_check(tempfilep, "r");
init_lexer(options.config_filep, fp, tempfilep);
parse_debug(); /* process any debug directives */
parse_systems();
fclose(fp);
unlink(tempfilep); /* clean up - remove temp file */
if (options.dumpconfig)
dump_config(stdout);
}
/*
* Parse optional debug directive
*/
void parse_debug()
{
lexer_tok_t tok;
tok = lex_get_token();
if (tok != T_Token || !streq(lex.strp, "debug")) {
lex_unget();
return;
}
lex_get(T_L_Brace);
do {
tok = lex_get_token();
switch (tok) {
case T_Token:
if (streq(lex.strp, "save_state"))
parse_save_state();
else
lex_fatal("Unexpected string in debug directive\n");
break;
case T_R_Brace:
return;
default:
lex_fatal("Unexpected token in debug directive.\n");
}
} while (1);
}
void
parse_save_state()
{
lexer_tok_t tok;
lex_get(T_L_Brace);
do {
tok = lex_get_token();
switch (tok) {
case T_Token:
if (streq(lex.strp, "filename")) {
lex_get(T_String);
options.save_restore.filenamep = Xstrdup(lex.strp);
printf("debug:save_state:filename = %s\n",
options.save_restore.filenamep);
} else
if (streq(lex.strp, "format")) {
lex_get(T_String);
if (streq(lex.strp, "legion"))
options.save_restore.legion_format = true;
else if (streq(lex.strp, "axis"))
options.save_restore.legion_format = false;
else
lex_fatal("expected either legion or axis");
printf("debug:save_state:format = %s\n",
options.save_restore.legion_format ? "legion" : "axis");
} else
if (streq(lex.strp, "icount")) {
lex_get(T_Number);
options.save_restore.trigger_icount = lex.val;
printf("debug:save_state:icount = 0x%llx\n",
options.save_restore.trigger_icount);
} else
if (streq(lex.strp, "trap_pc")) {
lex_get(T_Number);
options.save_restore.trap_pc = lex.val;
printf("debug:save_state:trap_pc = 0x%llx\n",
options.save_restore.trap_pc);
} else
if (streq(lex.strp, "patch")) {
lex_get(T_Number); /* addr */
options.save_restore.mem_patch.addr = lex.val;
lex_get(T_Number); /* val */
options.save_restore.mem_patch.val = lex.val;
printf("debug:save_state:patch addr=0x%llx with val=0x%llx\n",
options.save_restore.mem_patch.addr,
options.save_restore.mem_patch.val);
}
lex_get(T_S_Colon);
break;
case T_R_Brace:
return;
default:
lex_fatal("Unexpected token in save_state directive.\n");
}
} while (1);
}
/*
* Top level of parser ... look for one or more
* system directives.
*/
void parse_systems()
{
LIST_INIT( target_config.systems, system_t );
do {
system_t * systemp;
lexer_tok_t tok;
int idx;
tok = lex_get_token();
if (tok == T_EOF) break;
if (tok != T_Token || !streq(lex.strp, "system"))
lex_fatal("system definition expected");
lex_get(T_String);
idx = target_config.systems.count;
systemp = LIST_ADD( target_config.systems, system_t );
systemp->namep = Xstrdup(lex.strp);
systemp->idx = idx;
systemp->fake_sp = true;
lex_get(T_L_Brace);
parse_service_processor(systemp); /* if any */
parse_domains(systemp);
lex_get(T_R_Brace);
} while (1);
if (target_config.systems.count == 0) lex_fatal("at least one system expected");
}
/*
* Read one service_processor into the specified system, if it exists
*/
void parse_service_processor(system_t *systemp)
{
domain_t *domainp;
config_dev_t *config_devp;
lexer_tok_t tok;
char symname[BUFSIZ], libname[BUFSIZ], mdlname[BUFSIZ];
dev_type_t *dev_typep = NULL;
tok = lex_get_token();
if (tok == T_EOF)
lex_fatal("unexpected EOF");
if (tok == T_R_Brace)
lex_fatal("service_processor specific token expected");
if (tok != T_Token || !streq(lex.strp, "service_processor")) {
/* No Service processor - we're done */
lex_unget();
return;
}
lex_get(T_String); /* get the name of the service_proc */
sprintf(mdlname, "%s", lex.strp);
sprintf(symname, "dev_type_%s", mdlname);
DBG( printf("parse_service_processor - parsed %s - looking for %s\n",
mdlname, symname); );
dev_typep = (dev_type_t *)dlopen_lib(mdlname, symname, libname);
if (dev_typep == (dev_type_t *)0)
lex_fatal("unknown dev type \"%s\"", mdlname);
systemp->service_procp = Xmalloc(sizeof (service_proc_t));
/* parse service_processor */
dev_typep->parse_dev(NULL);
/* save pointer to the service_processor handle */
systemp->service_procp->dev_typep = dev_typep;
systemp->service_procp->namep = Xstrdup(mdlname);
#ifdef NOT_NOW
/* Turn off fake SP flag */
systemp->fake_sp = false;
#endif
return;
}
/*
* Read one or more domains into the specified system
*/
void parse_domains(system_t *systemp)
{
LIST_INIT(systemp->domains, domain_t);
do {
domain_t * domainp;
lexer_tok_t tok;
int idx;
tok = lex_get_token();
if (tok == T_EOF) lex_fatal("unexpected EOF");
if (tok == T_R_Brace) break;
if (tok != T_Token || !streq(lex.strp, "domain"))
lex_fatal("domain definition expected");
idx = systemp->domains.count;
domainp = LIST_ADD( systemp->domains, domain_t );
domainp->idx = idx;
domainp->systemp = systemp;
lex_get(T_L_Brace);
parse_domain(domainp);
lex_get(T_R_Brace);
} while (1);
if (systemp->domains.count == 0) lex_fatal("At least one domain expected for system \"%s\"", systemp->namep);
lex_unget();
}
/*
* OK, the more complicated function
* to determine and connect the different
* components of a domain.
*/
void parse_domain(domain_t *domainp)
{
LIST_INIT( domainp->procs, config_proc_t );
domainp->device.count = 0;
domainp->device.listp = (config_dev_t *)0;
do {
lexer_tok_t tok;
tok = lex_get_token();
if (tok == T_EOF) lex_fatal("unexpected EOF within domain defn");
if (tok == T_R_Brace) break;
if (tok != T_Token) goto fail;
if (streq(lex.strp, "sysclkfreq")) {
domainp->sysclkfreq = parse_number_assign();
} else
if (streq(lex.strp, "processor")) {
parse_proc(domainp);
} else
if (streq(lex.strp, "addressmap")) {
parse_addressmap(domainp);
} else {
fail:
lex_fatal("domain specific token expected");
}
} while (1);
lex_unget();
if (domainp->sysclkfreq == 0) lex_fatal("sysclkfreq not specified in domain definition");
/* processor specific domain check of stuff parsed from conf file */
LIST_ENTRY(domainp->procs, 0)->proc_typep->domain_check(domainp);
}
/*
* Handle the proc "name" wrapper around a processor definition.
* This enables us to select the correct processor handler
* then call into that handler to complete the parsing
* of this specific processor.
*/
void parse_proc(domain_t *domainp)
{
proc_type_t * proc_typep;
config_proc_t * config_procp;
lex_get(T_String); /* get the name of the proc type */
proc_typep = find_proc_type(lex.strp);
if (proc_typep == (proc_type_t *)0) lex_fatal("unknown processor type \"%s\"", lex.strp);
/*
* If the processor type module has not been loaded and
* initialised .. do so now
*/
if (!proc_typep->flag_initialised) {
if (!proc_typep->init_module(proc_typep)) fatal("Failed initialising processor module %s", proc_typep->proc_type_namep);
}
lex_get(T_L_Brace);
/* OK, allocate the proc entry */
config_procp = LIST_ADD( domainp->procs, config_proc_t );
config_procp->proc_id = domainp->procs.count -1;
config_procp->proc_typep = proc_typep;
config_procp->domainp = domainp;
/* based on the proc type we call the proc-dependent parser */
proc_typep->parse_proc(domainp, config_procp);
/* finish up cleanly */
lex_get(T_R_Brace);
}
/*
* This function (eventually) parses the address map
* for each domain. Basically, this creates the devices
* that are attached directly to the domain memory bus.
*
* Unnecessary ? FIXME
*/
void parse_addressmap(domain_t *domainp)
{
lexer_tok_t tok;
lex_get(T_L_Brace);
/* for now - just swallow until the } */
while (1) {
tok = lex_get_token();
if (tok == T_EOF) lex_fatal("unexpected end of file");
if (tok == T_R_Brace) break;
if (tok != T_Token) lex_fatal("addressmap element expected");
if (streq(lex.strp, "device")) {
parse_device(domainp);
} else if (streq(lex.strp, "mmi_device")) {
parse_mmi_device(domainp);
} else {
lex_fatal("unknown/unexpected token %s in addressmap", lex.strp);
}
} while (tok != T_EOF && tok != T_R_Brace);
lex_unget();
/* finish up cleanly */
lex_get(T_R_Brace);
}
/*
* having decided the address map entry is a device,
* parse its name, base, and extent, then drop into the
* device's own parsing routine.
*/
void parse_device(domain_t *domainp)
{
config_dev_t * config_devp, *overlapp;
config_addr_t *config_addrp;
dev_type_t * dev_typep;
bool_t is_size;
lexer_tok_t tok;
/*
* We've found a device name so check whether we have a library
* for this dev_type.
*/
lex_get(T_String);
dev_typep = find_dev_type(lex.strp);
if (dev_typep == (dev_type_t *)0) lex_fatal("unknown dev type \"%s\"",lex.strp);
/* OK, allocate the proc entry */
config_devp = Xmalloc(sizeof (config_dev_t));
config_devp->is_implied = false; /* this is a real device */
config_devp->dev_typep = dev_typep;
config_devp->devp = (void*)0; /* gets allocated in the dev parser */
config_devp->addrp = NULL; /* see insert_domain_address() */
do {
tpaddr_t baseaddr, topaddr;
lex_get(T_Number);
baseaddr = lex.val;
is_size = false;
tok = lex_get_token();
if (tok == T_Plus) is_size = true; else lex_unget();
lex_get(T_Number);
topaddr = lex.val;
if (is_size)
topaddr += baseaddr;
if (topaddr <= baseaddr)
lex_fatal("top address <= base address with device %s",
config_devp->dev_typep->dev_type_namep);
insert_domain_address(domainp, config_devp, baseaddr, topaddr);
} while (lex_get_token() == T_Comma);
lex_unget();
/* Insert device into the domain */
overlapp = insert_domain_device(domainp, config_devp);
if (overlapp != NULL) {
lex_fatal("device \"%s\" @ 0x%llx overlaps with device \"%s\" @ 0x%llx",
overlapp->dev_typep->dev_type_namep,
overlapp->addrp->baseaddr,
config_devp->dev_typep->dev_type_namep,
config_devp->addrp->baseaddr);
}
/* based on the dev type we call the dev-dependent parser */
dev_typep->parse_dev(config_devp);
}
/*
* Parse MMI device
*/
void parse_mmi_device(domain_t *domainp)
{
config_dev_t * config_devp, *overlapp;
config_addr_t *config_addrp;
dev_type_t * dev_typep;
lexer_tok_t tok;
config_devp = Xmalloc(sizeof (config_dev_t));
/*
* parse to get the MMI device name
*/
lex_get(T_String);
if (lex.strp == NULL)
lex_fatal("missing MMI device name in parse_mmi_device");
sam_init_device(lex.strp, config_devp);
/*
* finish the parsing
*/
sam_parse_dev(domainp, config_devp);
/*
* load MMI SAM device module
*/
dev_typep = sam_load_device(config_devp);
if (dev_typep == (dev_type_t *)0) lex_fatal("unknown dev type \"%s\"",lex.strp);
/*
* Found a matching library, add it to the loaded list.
*
* NOTE: We do not dlclose() the libarary here as we know it will be
* needed later on when the functions in the dev_typep are accessed.
*/
LIST_ADD_PTR(dev_cache, dev_type_t, dev_typep);
}
/*
* Support functions for other parser codes
*/
/*
* Parse a number assignment:
* e.g. 0x40000 ;
*/
uint64_t parse_number_assign()
{
uint64_t val;
lex_get(T_Number);
val = lex.val;
lex_get(T_S_Colon);
return (val);
}
/*
* dlopen a lib["mdlnamep"].so in the plugins/ dir or in the
* options.libpath search path and locate the symbol "symname".
* If there is a pre-existing handle - use it.
* If the library was successfully opened, save the handle.
* Once a valid handle is avail, get the address of symname and return.
* Don't log error on failure to add to htable; non-fatal, only affects
* performance.
*/
#define MAX_DLENT 128 /* max dlentries cacheable */
typedef struct {
char *so_path; /* fq path to lib where symbol found */
void *so_addr; /* shared object symbol address */
} dlitem_t;
void *
dlopen_lib(char *mdlnamep, char *symname, char *libname)
{
static int first_pass = 0;
void *dlh = NULL, *fptr = NULL;
int idx;
char *pathp;
ENTRY item, *f_item;
char hkey[128];
dlitem_t *dip;
if (first_pass == 0) {
(void) hcreate(MAX_DLENT);
first_pass = 1;
}
sprintf(hkey, "%s%s", mdlnamep, symname);
item.key = hkey;
DBG( printf("Looking for module [%s][%s]\n", mdlnamep, symname); );
if ((f_item = hsearch(item, FIND)) != NULL) {
/* found it, return info */
dip = (dlitem_t *)f_item->data;
strcpy(libname, dip->so_path);
DBG( printf("Found [%s][%s] %p\n",
mdlnamep, symname, dip->so_addr); );
return (dip->so_addr);
}
/* not found, try to open and add it on libpath */
for (idx = 0; idx < options.libpath.count; idx++) {
pathp = LIST_ENTRY(options.libpath, idx);
sprintf(libname, "%s/lib%s.so", pathp, mdlnamep);
DBG( printf("Loading the [%s] module - %s\n", mdlnamep, libname); );
if (NULL != (dlh = dlopen(libname, RTLD_NOW))) {
goto dlopen_success;
}
}
/* not found, try to open and add it on compiled in default paths */
sprintf(libname, "lib%s.so", mdlnamep);
DBG( printf("Loading the [%s] module - %s\n", mdlnamep, libname); );
if (NULL == (dlh = dlopen(libname, RTLD_NOW))) {
printf("\tdlopen_lib error opening [%s]\t%s\n",
mdlnamep, dlerror());
return (NULL);
}
dlopen_success:
/* Found a library in libpath that we can open */
DBG( printf("\tFound and opened [%s]\n", libname); );
/* Extract and return the symbol address from the shared object */
if (NULL == (fptr = dlsym(dlh, symname))) {
printf("\tdlopen_lib: [%s]\n", dlerror());
/* do not add entry if no symbol found */
return (NULL);
}
/* Have a valid lib,symbol - add to lookup table */
dip = Xmalloc(sizeof (*dip));
dip->so_path = Xstrdup(libname);
dip->so_addr = (void *)fptr;
item.data = (void *)dip;
item.key = Xstrdup(hkey);
/* add item to lookup table */
if (NULL == hsearch(item, ENTER)) {
free((void *)dip->so_path);
free(item.data);
free((void *)item.key);
}
return (fptr);
}
/*
* Call dlopen_lib() to load the device module lib["devnamep"].so in
* the plugins/ dir. If a matching library is found:
*
* - use dlsym() to look for it's dev_type_t structure
* - check the dev_type_t name value matches the "devnamep"
* - add the matching library to the loaded list
* - return a pointer to the dev_type_t for this device
*/
dev_type_t * find_dev_type(char *devnamep)
{
char symname[BUFSIZ], libname[BUFSIZ];
dev_type_t *dev_typep = NULL;
int idx;
if (devnamep == NULL)
return ((dev_type_t *)0);
/*
* First look through the cached list of device
* modules we've already opened.
*/
for (idx = 0; idx < dev_cache.count; idx++) {
dev_typep = LIST_ENTRY(dev_cache, idx);
if (streq(dev_typep->dev_type_namep, devnamep))
return (dev_typep);
}
/*
* Dlopen the device module library
*/
sprintf(symname, "dev_type_%s", devnamep);
dev_typep = (dev_type_t *)dlopen_lib(devnamep, symname, libname);
if (dev_typep == NULL)
return ((dev_type_t *)0);
/*
* Make sure the name in the dev_type_name matches the device
*/
if (!streq(dev_typep->dev_type_namep, devnamep)) {
printf("\nERROR: name does not match. %s != %s in %s:%s\n",
dev_typep->dev_type_namep, devnamep, libname, symname);
return ((dev_type_t *)0);
}
/*
* Found a matching library, add it to the loaded list.
*
* NOTE: We do not dlclose() the libarary here as we know it will be
* needed later on when the functions in the dev_typep are accessed.
*/
LIST_ADD_PTR(dev_cache, dev_type_t, dev_typep);
return (dev_typep);
}
/*
* Call dlopen_lib() to load the proc module lib["procnamep"].so in
* the plugins/ dir. If a matching library is found:
*
* - use dlsym() to look for it's proc_type_t structure
* - check the proc_type_t name value matches the "procnamep"
* - add the matching library to the loaded list
* - return a pointer to the proc_type_t for this proc
*/
proc_type_t * find_proc_type(char *procnamep)
{
char symname[BUFSIZ], libname[BUFSIZ];
proc_type_t *proc_typep = NULL;
int idx;
if (procnamep == NULL)
return ((proc_type_t *)0);
/* First look through the cached list of processor
* modules we've already opened.
*/
for (idx = 0; idx < proc_cache.count; idx++) {
proc_typep = LIST_ENTRY(proc_cache, idx);
if (streq(proc_typep->proc_type_namep, procnamep))
return (proc_typep);
}
/*
* Dlopen the proc module library
*/
sprintf(symname, "proc_type_%s", procnamep);
proc_typep = (proc_type_t *)dlopen_lib(procnamep, symname, libname);
if (proc_typep == NULL)
return ((proc_type_t *)0);
/*
* Make sure the name in the proc_type_name matches the device
*/
if (!streq(proc_typep->proc_type_namep, procnamep)) {
printf("\nERROR: name does not match. %s != %s in %s:%s\n",
proc_typep->proc_type_namep, procnamep, libname, symname);
return ((proc_type_t *)0);
}
/*
* Found a matching library, add it to the loaded list.
*
* NOTE: We do not dlclose() the libarary here as we know it will be
* needed later on when the functions in the proc_typep are accessed.
*/
LIST_ADD_PTR(proc_cache, proc_type_t, proc_typep);
return (proc_typep);
}
/*
* Insert a config device into the address map of a certain domain
* Returns NULL on success, the overlapping device node on failure.
*/
config_dev_t *
insert_domain_device(domain_t *domainp, config_dev_t *config_devp)
{
config_dev_t **pdp, *dp;
config_devp->domainp = domainp;
config_devp->device_id = domainp->device.count;
ASSERT(config_devp->addrp->topaddr > config_devp->addrp->baseaddr);
ASSERT(config_devp->addrp->range != (tvaddr_t)0);
/* insert into sorted list of devices ... */
for (pdp = &(domainp->device.listp); (dp = *pdp) != (config_dev_t *)0; pdp = &(dp->nextp)) {
if (dp->addrp->topaddr <= config_devp->addrp->baseaddr) continue;
if (dp->addrp->baseaddr >= config_devp->addrp->topaddr) break;
return (dp);
}
/* link device into list in order */
config_devp->nextp = *pdp;
*pdp = config_devp;
domainp->device.count ++;
DBG( printf("insert_domain_device %p name=<%s> base=%p, top=%p\n",
config_devp, config_devp->dev_typep->dev_type_namep,
config_devp->addrp->baseaddr,
config_devp->addrp->topaddr); );
return (NULL);
}
/*
* Insert a config address into the address map of a certain domain
* Returns NULL on success, the overlapping device node on failure.
*/
config_addr_t *
insert_domain_address(domain_t *domainp, config_dev_t *devp, tpaddr_t baseaddr, tpaddr_t topaddr)
{
config_addr_t **pap, *ap, *addrp;
addrp = Xmalloc(sizeof (config_addr_t));
addrp->config_devp = devp;
addrp->baseaddr = baseaddr;
addrp->topaddr = topaddr;
/* save us annoying adds and subs all the time */
addrp->range = topaddr - baseaddr;
if (devp->addrp == NULL)
devp->addrp = addrp;
/* insert into sorted list of addresses ... */
for (pap = &(domainp->address.listp); (ap = *pap) != NULL;
pap = &(ap->nextp)) {
if (ap->topaddr <= addrp->baseaddr) continue;
if (ap->baseaddr >= addrp->topaddr) break;
return (ap);
}
/* link device into list in order */
addrp->nextp = *pap;
*pap = addrp;
domainp->address.count++;
DBG( printf("insert_domain_address devp=%p addrp=%p base=%llx, "
"top=%llx\n",
addrp->config_devp, addrp, addrp->baseaddr,
addrp->topaddr); );
return (NULL);
}