6efef468be
2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * cris/configure: Regenerate. sim/common: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * aclocal.m4: Include ../../config/acx.m4. * common.m4: Use ACX_PKGVERSION and ACX_BUGURL. * configure, config.in: Regenerate. * Make-common.in (LIB_OBJS): Add version.o. (version.c, version.o): New rules. * run.c: Include version.h. (usage): Add help parameter. Print output either to stdout or stderr depending on that parameter. (print_version): New. (main): Check for --help and --version. * run-sim.h (sim_target_display_usage): Add help parameter. * version.h: New. sim/arm: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. * wrapper.c (sim_target_display_usage): Add help parameter. sim/cr16: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/d10v: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/erc32: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/frv: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/h8300: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/iq2000: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/m32c: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/m32r: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/m68hc11: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/mcore: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/mips: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/mn10300: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/ppc: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure.ac: Use ACX_PKGVERSION and ACX_BUGURL. * configure, config.in: Regenerated. * Makefile.in (LIB_OBJ): Add version.o. (version.c, version.o): New rules. * psim.c (psim_usage): Add help parameter. Print the bug URL. Exit with code 0 for help. (psim_options): Update calls to psim_usage. Handle --help and --version. * psim.h (psim_usage): Update prototype. * main.c (main): Update psim_usage call. sim/sh: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/sh64: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate. sim/v850: 2008-06-06 Vladimir Prus <vladimir@codesourcery.com> Daniel Jacobowitz <dan@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * configure: Regenerate.
1213 lines
32 KiB
C
1213 lines
32 KiB
C
/* This file is part of the program psim.
|
|
|
|
Copyright 1994, 1995, 1996, 1997, 2003 Andrew Cagney
|
|
|
|
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; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
#ifndef _PSIM_C_
|
|
#define _PSIM_C_
|
|
|
|
#include "cpu.h" /* includes psim.h */
|
|
#include "idecode.h"
|
|
#include "options.h"
|
|
|
|
#include "tree.h"
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include <setjmp.h>
|
|
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#include "bfd.h"
|
|
#include "libiberty.h"
|
|
#include "gdb/signals.h"
|
|
|
|
/* system structure, actual size of processor array determined at
|
|
runtime */
|
|
|
|
struct _psim {
|
|
event_queue *events;
|
|
device *devices;
|
|
mon *monitor;
|
|
os_emul *os_emulation;
|
|
core *memory;
|
|
|
|
/* escape routine for inner functions */
|
|
void *path_to_halt;
|
|
void *path_to_restart;
|
|
|
|
/* status from last halt */
|
|
psim_status halt_status;
|
|
|
|
/* the processors proper */
|
|
int nr_cpus;
|
|
int last_cpu; /* CPU that last (tried to) execute an instruction */
|
|
cpu *processors[MAX_NR_PROCESSORS];
|
|
};
|
|
|
|
|
|
int current_target_byte_order;
|
|
int current_host_byte_order;
|
|
int current_environment;
|
|
int current_alignment;
|
|
int current_floating_point;
|
|
int current_model_issue = MODEL_ISSUE_IGNORE;
|
|
int current_stdio = DO_USE_STDIO;
|
|
model_enum current_model = WITH_DEFAULT_MODEL;
|
|
|
|
|
|
/* create the device tree */
|
|
|
|
INLINE_PSIM\
|
|
(device *)
|
|
psim_tree(void)
|
|
{
|
|
device *root = tree_parse(NULL, "core");
|
|
tree_parse(root, "/aliases");
|
|
tree_parse(root, "/options");
|
|
tree_parse(root, "/chosen");
|
|
tree_parse(root, "/packages");
|
|
tree_parse(root, "/cpus");
|
|
tree_parse(root, "/openprom");
|
|
tree_parse(root, "/openprom/init");
|
|
tree_parse(root, "/openprom/trace");
|
|
tree_parse(root, "/openprom/options");
|
|
return root;
|
|
}
|
|
|
|
STATIC_INLINE_PSIM\
|
|
(char *)
|
|
find_arg(char *err_msg,
|
|
int *ptr_to_argp,
|
|
char **argv)
|
|
{
|
|
*ptr_to_argp += 1;
|
|
if (argv[*ptr_to_argp] == NULL)
|
|
error(err_msg);
|
|
return argv[*ptr_to_argp];
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_usage(int verbose, int help)
|
|
{
|
|
printf_filtered("Usage:\n");
|
|
printf_filtered("\n");
|
|
printf_filtered("\tpsim [ <psim-option> ... ] <image> [ <image-arg> ... ]\n");
|
|
printf_filtered("\n");
|
|
printf_filtered("Where\n");
|
|
printf_filtered("\n");
|
|
printf_filtered("\t<image> Name of the PowerPC program to run.\n");
|
|
if (verbose) {
|
|
printf_filtered("\t This can either be a PowerPC binary or\n");
|
|
printf_filtered("\t a text file containing a device tree\n");
|
|
printf_filtered("\t specification.\n");
|
|
printf_filtered("\t PSIM will attempt to determine from the\n");
|
|
printf_filtered("\t specified <image> the intended emulation\n");
|
|
printf_filtered("\t environment.\n");
|
|
printf_filtered("\t If PSIM gets it wrong, the emulation\n");
|
|
printf_filtered("\t environment can be specified using the\n");
|
|
printf_filtered("\t `-e' option (described below).\n");
|
|
printf_filtered("\n"); }
|
|
printf_filtered("\t<image-arg> Argument to be passed to <image>\n");
|
|
if (verbose) {
|
|
printf_filtered("\t These arguments will be passed to\n");
|
|
printf_filtered("\t <image> (as standard C argv, argc)\n");
|
|
printf_filtered("\t when <image> is started.\n");
|
|
printf_filtered("\n"); }
|
|
printf_filtered("\t<psim-option> See below\n");
|
|
printf_filtered("\n");
|
|
printf_filtered("The following are valid <psim-option>s:\n");
|
|
printf_filtered("\n");
|
|
|
|
printf_filtered("\t-c <count> Limit the simulation to <count> iterations\n");
|
|
if (verbose) {
|
|
printf_filtered("\n");
|
|
}
|
|
|
|
printf_filtered("\t-i or -i2 Print instruction counting statistics\n");
|
|
if (verbose) {
|
|
printf_filtered("\t Specify -i2 for a more detailed display\n");
|
|
printf_filtered("\n");
|
|
}
|
|
|
|
printf_filtered("\t-I Print execution unit statistics\n");
|
|
if (verbose) { printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-e <os-emul> specify an OS or platform to model\n");
|
|
if (verbose) {
|
|
printf_filtered("\t Can be any of the following:\n");
|
|
printf_filtered("\t bug - OEA + MOTO BUG ROM calls\n");
|
|
printf_filtered("\t netbsd - UEA + NetBSD system calls\n");
|
|
printf_filtered("\t solaris - UEA + Solaris system calls\n");
|
|
printf_filtered("\t linux - UEA + Linux system calls\n");
|
|
printf_filtered("\t chirp - OEA + a few OpenBoot calls\n");
|
|
printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-E <endian> Specify the endianness of the target\n");
|
|
if (verbose) {
|
|
printf_filtered("\t Can be any of the following:\n");
|
|
printf_filtered("\t big - big endian target\n");
|
|
printf_filtered("\t little - little endian target\n");
|
|
printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-f <file> Merge <file> into the device tree\n");
|
|
if (verbose) { printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-h -? -H give more detailed usage\n");
|
|
if (verbose) { printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-m <model> Specify the processor to model (604)\n");
|
|
if (verbose) {
|
|
printf_filtered("\t Selects the processor to use when\n");
|
|
printf_filtered("\t modeling execution units. Includes:\n");
|
|
printf_filtered("\t 604, 603 and 603e\n");
|
|
printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-n <nr-smp> Specify the number of processors in SMP simulations\n");
|
|
if (verbose) {
|
|
printf_filtered("\t Specifies the number of processors that are\n");
|
|
printf_filtered("\t to be modeled in a symetric multi-processor (SMP)\n");
|
|
printf_filtered("\t simulation\n");
|
|
printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-o <dev-spec> Add device <dev-spec> to the device tree\n");
|
|
if (verbose) { printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-r <ram-size> Set RAM size in bytes (OEA environments)\n");
|
|
if (verbose) { printf_filtered("\n"); }
|
|
|
|
printf_filtered("\t-t [!]<trace> Enable (disable) <trace> option\n");
|
|
if (verbose) { printf_filtered("\n"); }
|
|
|
|
printf_filtered("\n");
|
|
trace_usage(verbose);
|
|
device_usage(verbose);
|
|
if (verbose > 1) {
|
|
printf_filtered("\n");
|
|
print_options();
|
|
}
|
|
|
|
if (REPORT_BUGS_TO[0])
|
|
printf ("Report bugs to %s\n", REPORT_BUGS_TO);
|
|
exit (help ? 0 : 1);
|
|
}
|
|
|
|
/* Test "string" for containing a string of digits that form a number
|
|
between "min" and "max". The return value is the number or "err". */
|
|
static
|
|
int is_num( char *string, int min, int max, int err)
|
|
{
|
|
int result = 0;
|
|
|
|
for ( ; *string; ++string)
|
|
{
|
|
if (!isdigit(*string))
|
|
{
|
|
result = err;
|
|
break;
|
|
}
|
|
result = result * 10 + (*string - '0');
|
|
}
|
|
if (result < min || result > max)
|
|
result = err;
|
|
|
|
return result;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(char **)
|
|
psim_options(device *root,
|
|
char **argv)
|
|
{
|
|
device *current = root;
|
|
int argp;
|
|
if (argv == NULL)
|
|
return NULL;
|
|
argp = 0;
|
|
while (argv[argp] != NULL && argv[argp][0] == '-') {
|
|
char *p = argv[argp] + 1;
|
|
char *param;
|
|
while (*p != '\0') {
|
|
switch (*p) {
|
|
default:
|
|
psim_usage(0, 0);
|
|
error ("");
|
|
break;
|
|
case 'c':
|
|
param = find_arg("Missing <count> option for -c (max-iterations)\n", &argp, argv);
|
|
tree_parse(root, "/openprom/options/max-iterations %s", param);
|
|
break;
|
|
case 'e':
|
|
param = find_arg("Missing <emul> option for -e (os-emul)\n", &argp, argv);
|
|
tree_parse(root, "/openprom/options/os-emul %s", param);
|
|
break;
|
|
case 'E':
|
|
/* endian spec, ignored for now */
|
|
param = find_arg("Missing <endian> option for -E (target-endian)\n", &argp, argv);
|
|
if (strcmp (param, "big") == 0)
|
|
tree_parse (root, "/options/little-endian? false");
|
|
else if (strcmp (param, "little") == 0)
|
|
tree_parse (root, "/options/little-endian? true");
|
|
else
|
|
{
|
|
printf_filtered ("Invalid <endian> option for -E (target-endian)\n");
|
|
psim_usage (0, 0);
|
|
}
|
|
break;
|
|
case 'f':
|
|
param = find_arg("Missing <file> option for -f\n", &argp, argv);
|
|
psim_merge_device_file(root, param);
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
psim_usage(1, 1);
|
|
break;
|
|
case 'H':
|
|
psim_usage(2, 1);
|
|
break;
|
|
case 'i':
|
|
if (isdigit(p[1])) {
|
|
tree_parse(root, "/openprom/trace/print-info %c", p[1]);
|
|
p++;
|
|
}
|
|
else {
|
|
tree_parse(root, "/openprom/trace/print-info 1");
|
|
}
|
|
break;
|
|
case 'I':
|
|
tree_parse(root, "/openprom/trace/print-info 2");
|
|
tree_parse(root, "/openprom/options/model-issue %d",
|
|
MODEL_ISSUE_PROCESS);
|
|
break;
|
|
case 'm':
|
|
param = find_arg("Missing <model> option for -m (model)\n", &argp, argv);
|
|
tree_parse(root, "/openprom/options/model \"%s", param);
|
|
break;
|
|
case 'n':
|
|
param = find_arg("Missing <nr-smp> option for -n (smp)\n", &argp, argv);
|
|
tree_parse(root, "/openprom/options/smp %s", param);
|
|
break;
|
|
case 'o':
|
|
param = find_arg("Missing <dev-spec> option for -o\n", &argp, argv);
|
|
if (memcmp(param, "mpc860c0", 8) == 0)
|
|
{
|
|
if (param[8] == '\0')
|
|
tree_parse(root, "/options/mpc860c0 5");
|
|
else if (param[8] == '=' && is_num(param+9, 1, 10, 0))
|
|
{
|
|
tree_parse(root, "/options/mpc860c0 %s", param+9);
|
|
}
|
|
else error("Invalid mpc860c0 option for -o\n");
|
|
}
|
|
else
|
|
current = tree_parse(current, "%s", param);
|
|
break;
|
|
case 'r':
|
|
param = find_arg("Missing <ram-size> option for -r (oea-memory-size)\n", &argp, argv);
|
|
tree_parse(root, "/openprom/options/oea-memory-size %s",
|
|
param);
|
|
break;
|
|
case 't':
|
|
param = find_arg("Missing <trace> option for -t (trace/*)\n", &argp, argv);
|
|
if (param[0] == '!')
|
|
tree_parse(root, "/openprom/trace/%s 0", param+1);
|
|
else
|
|
tree_parse(root, "/openprom/trace/%s 1", param);
|
|
break;
|
|
case '-':
|
|
/* it's a long option of the form --optionname=optionvalue.
|
|
Such options can be passed through if we are invoked by
|
|
gdb. */
|
|
if (strstr(argv[argp], "architecture") != NULL) {
|
|
/* we must consume the argument here, so that we get out
|
|
of the loop. */
|
|
p = argv[argp] + strlen(argv[argp]) - 1;
|
|
printf_filtered("Warning - architecture parameter ignored\n");
|
|
}
|
|
else if (strcmp (argv[argp], "--help") == 0)
|
|
psim_usage (0, 1);
|
|
else if (strcmp (argv[argp], "--version") == 0)
|
|
{
|
|
extern const char version[];
|
|
printf ("GNU simulator %s%s\n", PKGVERSION, version);
|
|
exit (0);
|
|
}
|
|
else
|
|
error("Unrecognized option");
|
|
break;
|
|
}
|
|
p += 1;
|
|
}
|
|
argp += 1;
|
|
}
|
|
/* force the trace node to process its options now *before* the tree
|
|
initialization occures */
|
|
device_ioctl(tree_find_device(root, "/openprom/trace"),
|
|
NULL, 0,
|
|
device_ioctl_set_trace);
|
|
|
|
{
|
|
void semantic_init(device* root);
|
|
semantic_init(root);
|
|
}
|
|
|
|
/* return where the options end */
|
|
return argv + argp;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_command(device *root,
|
|
char **argv)
|
|
{
|
|
int argp = 0;
|
|
if (argv[argp] == NULL) {
|
|
return;
|
|
}
|
|
else if (strcmp(argv[argp], "trace") == 0) {
|
|
const char *opt = find_arg("Missing <trace> option", &argp, argv);
|
|
if (opt[0] == '!')
|
|
trace_option(opt + 1, 0);
|
|
else
|
|
trace_option(opt, 1);
|
|
}
|
|
else if (strcmp(*argv, "change-media") == 0) {
|
|
char *device = find_arg("Missing device name", &argp, argv);
|
|
char *media = argv[++argp];
|
|
device_ioctl(tree_find_device(root, device), NULL, 0,
|
|
device_ioctl_change_media, media);
|
|
}
|
|
else {
|
|
printf_filtered("Unknown PSIM command %s, try\n", argv[argp]);
|
|
printf_filtered(" trace <trace-option>\n");
|
|
printf_filtered(" change-media <device> [ <new-image> ]\n");
|
|
}
|
|
}
|
|
|
|
|
|
/* create the simulator proper from the device tree and executable */
|
|
|
|
INLINE_PSIM\
|
|
(psim *)
|
|
psim_create(const char *file_name,
|
|
device *root)
|
|
{
|
|
int cpu_nr;
|
|
const char *env;
|
|
psim *system;
|
|
os_emul *os_emulation;
|
|
int nr_cpus;
|
|
|
|
/* given this partially populated device tree, os_emul_create() uses
|
|
it and file_name to determine the selected emulation and hence
|
|
further populate the tree with any other required nodes. */
|
|
|
|
os_emulation = os_emul_create(file_name, root);
|
|
if (os_emulation == NULL)
|
|
error("psim: either file %s was not reconized or unreconized or unknown os-emulation type\n", file_name);
|
|
|
|
/* fill in the missing real number of CPU's */
|
|
nr_cpus = tree_find_integer_property(root, "/openprom/options/smp");
|
|
if (MAX_NR_PROCESSORS < nr_cpus)
|
|
error("target and configured number of cpus conflict\n");
|
|
|
|
/* fill in the missing TARGET BYTE ORDER information */
|
|
current_target_byte_order
|
|
= (tree_find_boolean_property(root, "/options/little-endian?")
|
|
? LITTLE_ENDIAN
|
|
: BIG_ENDIAN);
|
|
if (CURRENT_TARGET_BYTE_ORDER != current_target_byte_order)
|
|
error("target and configured byte order conflict\n");
|
|
|
|
/* fill in the missing HOST BYTE ORDER information */
|
|
current_host_byte_order = (current_host_byte_order = 1,
|
|
(*(char*)(¤t_host_byte_order)
|
|
? LITTLE_ENDIAN
|
|
: BIG_ENDIAN));
|
|
if (CURRENT_HOST_BYTE_ORDER != current_host_byte_order)
|
|
error("host and configured byte order conflict\n");
|
|
|
|
/* fill in the missing OEA/VEA information */
|
|
env = tree_find_string_property(root, "/openprom/options/env");
|
|
current_environment = ((strcmp(env, "user") == 0
|
|
|| strcmp(env, "uea") == 0)
|
|
? USER_ENVIRONMENT
|
|
: (strcmp(env, "virtual") == 0
|
|
|| strcmp(env, "vea") == 0)
|
|
? VIRTUAL_ENVIRONMENT
|
|
: (strcmp(env, "operating") == 0
|
|
|| strcmp(env, "oea") == 0)
|
|
? OPERATING_ENVIRONMENT
|
|
: 0);
|
|
if (current_environment == 0)
|
|
error("unreconized /options env property\n");
|
|
if (CURRENT_ENVIRONMENT != current_environment)
|
|
error("target and configured environment conflict\n");
|
|
|
|
/* fill in the missing ALLIGNMENT information */
|
|
current_alignment
|
|
= (tree_find_boolean_property(root, "/openprom/options/strict-alignment?")
|
|
? STRICT_ALIGNMENT
|
|
: NONSTRICT_ALIGNMENT);
|
|
if (CURRENT_ALIGNMENT != current_alignment)
|
|
error("target and configured alignment conflict\n");
|
|
|
|
/* fill in the missing FLOATING POINT information */
|
|
current_floating_point
|
|
= (tree_find_boolean_property(root, "/openprom/options/floating-point?")
|
|
? HARD_FLOATING_POINT
|
|
: SOFT_FLOATING_POINT);
|
|
if (CURRENT_FLOATING_POINT != current_floating_point)
|
|
error("target and configured floating-point conflict\n");
|
|
|
|
/* fill in the missing STDIO information */
|
|
current_stdio
|
|
= (tree_find_boolean_property(root, "/openprom/options/use-stdio?")
|
|
? DO_USE_STDIO
|
|
: DONT_USE_STDIO);
|
|
if (CURRENT_STDIO != current_stdio)
|
|
error("target and configured stdio interface conflict\n");
|
|
|
|
/* sort out the level of detail for issue modeling */
|
|
current_model_issue
|
|
= tree_find_integer_property(root, "/openprom/options/model-issue");
|
|
if (CURRENT_MODEL_ISSUE != current_model_issue)
|
|
error("target and configured model-issue conflict\n");
|
|
|
|
/* sort out our model architecture - wrong.
|
|
|
|
FIXME: this should be obtaining the required information from the
|
|
device tree via the "/chosen" property "cpu" which is an instance
|
|
(ihandle) for the only executing processor. By converting that
|
|
ihandle into the corresponding cpu's phandle and then querying
|
|
the "name" property, the cpu type can be determined. Ok? */
|
|
|
|
model_set(tree_find_string_property(root, "/openprom/options/model"));
|
|
|
|
/* create things */
|
|
system = ZALLOC(psim);
|
|
system->events = event_queue_create();
|
|
system->memory = core_from_device(root);
|
|
system->monitor = mon_create();
|
|
system->nr_cpus = nr_cpus;
|
|
system->os_emulation = os_emulation;
|
|
system->devices = root;
|
|
|
|
/* now all the processors attaching to each their per-cpu information */
|
|
for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++) {
|
|
system->processors[cpu_nr] = cpu_create(system,
|
|
system->memory,
|
|
mon_cpu(system->monitor,
|
|
cpu_nr),
|
|
system->os_emulation,
|
|
cpu_nr);
|
|
}
|
|
|
|
/* dump out the contents of the device tree */
|
|
if (ppc_trace[trace_print_device_tree] || ppc_trace[trace_dump_device_tree])
|
|
tree_print(root);
|
|
if (ppc_trace[trace_dump_device_tree])
|
|
error("");
|
|
|
|
return system;
|
|
}
|
|
|
|
|
|
/* allow the simulation to stop/restart abnormaly */
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_set_halt_and_restart(psim *system,
|
|
void *halt_jmp_buf,
|
|
void *restart_jmp_buf)
|
|
{
|
|
system->path_to_halt = halt_jmp_buf;
|
|
system->path_to_restart = restart_jmp_buf;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_clear_halt_and_restart(psim *system)
|
|
{
|
|
system->path_to_halt = NULL;
|
|
system->path_to_restart = NULL;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_restart(psim *system,
|
|
int current_cpu)
|
|
{
|
|
ASSERT(current_cpu >= 0 && current_cpu < system->nr_cpus);
|
|
ASSERT(system->path_to_restart != NULL);
|
|
system->last_cpu = current_cpu;
|
|
longjmp(*(jmp_buf*)(system->path_to_restart), current_cpu + 1);
|
|
}
|
|
|
|
|
|
static void
|
|
cntrl_c_simulation(void *data)
|
|
{
|
|
psim *system = data;
|
|
psim_halt(system,
|
|
psim_nr_cpus(system),
|
|
was_continuing,
|
|
TARGET_SIGNAL_INT);
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_stop(psim *system)
|
|
{
|
|
event_queue_schedule_after_signal(psim_event_queue(system),
|
|
0 /*NOW*/,
|
|
cntrl_c_simulation,
|
|
system);
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_halt(psim *system,
|
|
int current_cpu,
|
|
stop_reason reason,
|
|
int signal)
|
|
{
|
|
ASSERT(current_cpu >= 0 && current_cpu <= system->nr_cpus);
|
|
ASSERT(system->path_to_halt != NULL);
|
|
system->last_cpu = current_cpu;
|
|
system->halt_status.reason = reason;
|
|
system->halt_status.signal = signal;
|
|
if (current_cpu == system->nr_cpus) {
|
|
system->halt_status.cpu_nr = 0;
|
|
system->halt_status.program_counter =
|
|
cpu_get_program_counter(system->processors[0]);
|
|
}
|
|
else {
|
|
system->halt_status.cpu_nr = current_cpu;
|
|
system->halt_status.program_counter =
|
|
cpu_get_program_counter(system->processors[current_cpu]);
|
|
}
|
|
longjmp(*(jmp_buf*)(system->path_to_halt), current_cpu + 1);
|
|
}
|
|
|
|
|
|
INLINE_PSIM\
|
|
(int)
|
|
psim_last_cpu(psim *system)
|
|
{
|
|
return system->last_cpu;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(int)
|
|
psim_nr_cpus(psim *system)
|
|
{
|
|
return system->nr_cpus;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(psim_status)
|
|
psim_get_status(psim *system)
|
|
{
|
|
return system->halt_status;
|
|
}
|
|
|
|
|
|
INLINE_PSIM\
|
|
(cpu *)
|
|
psim_cpu(psim *system,
|
|
int cpu_nr)
|
|
{
|
|
if (cpu_nr < 0 || cpu_nr >= system->nr_cpus)
|
|
return NULL;
|
|
else
|
|
return system->processors[cpu_nr];
|
|
}
|
|
|
|
|
|
INLINE_PSIM\
|
|
(device *)
|
|
psim_device(psim *system,
|
|
const char *path)
|
|
{
|
|
return tree_find_device(system->devices, path);
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(event_queue *)
|
|
psim_event_queue(psim *system)
|
|
{
|
|
return system->events;
|
|
}
|
|
|
|
|
|
|
|
STATIC_INLINE_PSIM\
|
|
(void)
|
|
psim_max_iterations_exceeded(void *data)
|
|
{
|
|
psim *system = data;
|
|
psim_halt(system,
|
|
system->nr_cpus, /* halted during an event */
|
|
was_signalled,
|
|
-1);
|
|
}
|
|
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_init(psim *system)
|
|
{
|
|
int cpu_nr;
|
|
|
|
/* scrub the monitor */
|
|
mon_init(system->monitor, system->nr_cpus);
|
|
|
|
/* trash any pending events */
|
|
event_queue_init(system->events);
|
|
|
|
/* if needed, schedule a halt event. FIXME - In the future this
|
|
will be replaced by a more generic change to psim_command(). A
|
|
new command `schedule NNN halt' being added. */
|
|
if (tree_find_property(system->devices, "/openprom/options/max-iterations")) {
|
|
event_queue_schedule(system->events,
|
|
tree_find_integer_property(system->devices,
|
|
"/openprom/options/max-iterations") - 2,
|
|
psim_max_iterations_exceeded,
|
|
system);
|
|
}
|
|
|
|
/* scrub all the cpus */
|
|
for (cpu_nr = 0; cpu_nr < system->nr_cpus; cpu_nr++)
|
|
cpu_init(system->processors[cpu_nr]);
|
|
|
|
/* init all the devices (which updates the cpus) */
|
|
tree_init(system->devices, system);
|
|
|
|
/* and the emulation (which needs an initialized device tree) */
|
|
os_emul_init(system->os_emulation, system->nr_cpus);
|
|
|
|
/* now sync each cpu against the initialized state of its registers */
|
|
for (cpu_nr = 0; cpu_nr < system->nr_cpus; cpu_nr++) {
|
|
cpu *processor = system->processors[cpu_nr];
|
|
cpu_synchronize_context(processor, cpu_get_program_counter(processor));
|
|
cpu_page_tlb_invalidate_all(processor);
|
|
}
|
|
|
|
/* force loop to start with first cpu */
|
|
system->last_cpu = -1;
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_stack(psim *system,
|
|
char **argv,
|
|
char **envp)
|
|
{
|
|
/* pass the stack device the argv/envp and let it work out what to
|
|
do with it */
|
|
device *stack_device = tree_find_device(system->devices,
|
|
"/openprom/init/stack");
|
|
if (stack_device != (device*)0) {
|
|
unsigned_word stack_pointer;
|
|
ASSERT (psim_read_register(system, 0, &stack_pointer, "sp",
|
|
cooked_transfer) > 0);
|
|
device_ioctl(stack_device,
|
|
NULL, /*cpu*/
|
|
0, /*cia*/
|
|
device_ioctl_create_stack,
|
|
stack_pointer,
|
|
argv,
|
|
envp);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* SIMULATE INSTRUCTIONS, various different ways of achieving the same
|
|
thing */
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_step(psim *system)
|
|
{
|
|
volatile int keep_running = 0;
|
|
idecode_run_until_stop(system, &keep_running,
|
|
system->events, system->processors, system->nr_cpus);
|
|
}
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_run(psim *system)
|
|
{
|
|
idecode_run(system,
|
|
system->events, system->processors, system->nr_cpus);
|
|
}
|
|
|
|
|
|
/* storage manipulation functions */
|
|
|
|
INLINE_PSIM\
|
|
(int)
|
|
psim_read_register(psim *system,
|
|
int which_cpu,
|
|
void *buf,
|
|
const char reg[],
|
|
transfer_mode mode)
|
|
{
|
|
register_descriptions description;
|
|
char *cooked_buf;
|
|
cpu *processor;
|
|
|
|
/* find our processor */
|
|
if (which_cpu == MAX_NR_PROCESSORS) {
|
|
if (system->last_cpu == system->nr_cpus
|
|
|| system->last_cpu == -1)
|
|
which_cpu = 0;
|
|
else
|
|
which_cpu = system->last_cpu;
|
|
}
|
|
ASSERT(which_cpu >= 0 && which_cpu < system->nr_cpus);
|
|
|
|
processor = system->processors[which_cpu];
|
|
|
|
/* find the register description */
|
|
description = register_description(reg);
|
|
if (description.type == reg_invalid)
|
|
return 0;
|
|
cooked_buf = alloca (description.size);
|
|
|
|
/* get the cooked value */
|
|
switch (description.type) {
|
|
|
|
case reg_gpr:
|
|
*(gpreg*)cooked_buf = cpu_registers(processor)->gpr[description.index];
|
|
break;
|
|
|
|
case reg_spr:
|
|
*(spreg*)cooked_buf = cpu_registers(processor)->spr[description.index];
|
|
break;
|
|
|
|
case reg_sr:
|
|
*(sreg*)cooked_buf = cpu_registers(processor)->sr[description.index];
|
|
break;
|
|
|
|
case reg_fpr:
|
|
*(fpreg*)cooked_buf = cpu_registers(processor)->fpr[description.index];
|
|
break;
|
|
|
|
case reg_pc:
|
|
*(unsigned_word*)cooked_buf = cpu_get_program_counter(processor);
|
|
break;
|
|
|
|
case reg_cr:
|
|
*(creg*)cooked_buf = cpu_registers(processor)->cr;
|
|
break;
|
|
|
|
case reg_msr:
|
|
*(msreg*)cooked_buf = cpu_registers(processor)->msr;
|
|
break;
|
|
|
|
case reg_fpscr:
|
|
*(fpscreg*)cooked_buf = cpu_registers(processor)->fpscr;
|
|
break;
|
|
|
|
case reg_insns:
|
|
*(unsigned_word*)cooked_buf = mon_get_number_of_insns(system->monitor,
|
|
which_cpu);
|
|
break;
|
|
|
|
case reg_stalls:
|
|
if (cpu_model(processor) == NULL)
|
|
error("$stalls only valid if processor unit model enabled (-I)\n");
|
|
*(unsigned_word*)cooked_buf = model_get_number_of_stalls(cpu_model(processor));
|
|
break;
|
|
|
|
case reg_cycles:
|
|
if (cpu_model(processor) == NULL)
|
|
error("$cycles only valid if processor unit model enabled (-I)\n");
|
|
*(unsigned_word*)cooked_buf = model_get_number_of_cycles(cpu_model(processor));
|
|
break;
|
|
|
|
#ifdef WITH_ALTIVEC
|
|
case reg_vr:
|
|
*(vreg*)cooked_buf = cpu_registers(processor)->altivec.vr[description.index];
|
|
break;
|
|
|
|
case reg_vscr:
|
|
*(vscreg*)cooked_buf = cpu_registers(processor)->altivec.vscr;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef WITH_E500
|
|
case reg_gprh:
|
|
*(gpreg*)cooked_buf = cpu_registers(processor)->e500.gprh[description.index];
|
|
break;
|
|
|
|
case reg_evr:
|
|
*(unsigned64*)cooked_buf = EVR(description.index);
|
|
break;
|
|
|
|
case reg_acc:
|
|
*(accreg*)cooked_buf = cpu_registers(processor)->e500.acc;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
printf_filtered("psim_read_register(processor=0x%lx,buf=0x%lx,reg=%s) %s\n",
|
|
(unsigned long)processor, (unsigned long)buf, reg,
|
|
"read of this register unimplemented");
|
|
break;
|
|
|
|
}
|
|
|
|
/* the PSIM internal values are in host order. To fetch raw data,
|
|
they need to be converted into target order and then returned */
|
|
if (mode == raw_transfer) {
|
|
/* FIXME - assumes that all registers are simple integers */
|
|
switch (description.size) {
|
|
case 1:
|
|
*(unsigned_1*)buf = H2T_1(*(unsigned_1*)cooked_buf);
|
|
break;
|
|
case 2:
|
|
*(unsigned_2*)buf = H2T_2(*(unsigned_2*)cooked_buf);
|
|
break;
|
|
case 4:
|
|
*(unsigned_4*)buf = H2T_4(*(unsigned_4*)cooked_buf);
|
|
break;
|
|
case 8:
|
|
*(unsigned_8*)buf = H2T_8(*(unsigned_8*)cooked_buf);
|
|
break;
|
|
#ifdef WITH_ALTIVEC
|
|
case 16:
|
|
if (CURRENT_HOST_BYTE_ORDER != CURRENT_TARGET_BYTE_ORDER)
|
|
{
|
|
union { vreg v; unsigned_8 d[2]; } h, t;
|
|
memcpy(&h.v/*dest*/, cooked_buf/*src*/, description.size);
|
|
{ _SWAP_8(t.d[0] =, h.d[1]); }
|
|
{ _SWAP_8(t.d[1] =, h.d[0]); }
|
|
memcpy(buf/*dest*/, &t/*src*/, description.size);
|
|
break;
|
|
}
|
|
else
|
|
memcpy(buf/*dest*/, cooked_buf/*src*/, description.size);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
memcpy(buf/*dest*/, cooked_buf/*src*/, description.size);
|
|
}
|
|
|
|
return description.size;
|
|
}
|
|
|
|
|
|
|
|
INLINE_PSIM\
|
|
(int)
|
|
psim_write_register(psim *system,
|
|
int which_cpu,
|
|
const void *buf,
|
|
const char reg[],
|
|
transfer_mode mode)
|
|
{
|
|
cpu *processor;
|
|
register_descriptions description;
|
|
char *cooked_buf;
|
|
|
|
/* find our processor */
|
|
if (which_cpu == MAX_NR_PROCESSORS) {
|
|
if (system->last_cpu == system->nr_cpus
|
|
|| system->last_cpu == -1)
|
|
which_cpu = 0;
|
|
else
|
|
which_cpu = system->last_cpu;
|
|
}
|
|
|
|
/* find the description of the register */
|
|
description = register_description(reg);
|
|
if (description.type == reg_invalid)
|
|
return 0;
|
|
cooked_buf = alloca (description.size);
|
|
|
|
if (which_cpu == -1) {
|
|
int i;
|
|
for (i = 0; i < system->nr_cpus; i++)
|
|
psim_write_register(system, i, buf, reg, mode);
|
|
return description.size;
|
|
}
|
|
ASSERT(which_cpu >= 0 && which_cpu < system->nr_cpus);
|
|
|
|
processor = system->processors[which_cpu];
|
|
|
|
/* If the data is comming in raw (target order), need to cook it
|
|
into host order before putting it into PSIM's internal structures */
|
|
if (mode == raw_transfer) {
|
|
switch (description.size) {
|
|
case 1:
|
|
*(unsigned_1*)cooked_buf = T2H_1(*(unsigned_1*)buf);
|
|
break;
|
|
case 2:
|
|
*(unsigned_2*)cooked_buf = T2H_2(*(unsigned_2*)buf);
|
|
break;
|
|
case 4:
|
|
*(unsigned_4*)cooked_buf = T2H_4(*(unsigned_4*)buf);
|
|
break;
|
|
case 8:
|
|
*(unsigned_8*)cooked_buf = T2H_8(*(unsigned_8*)buf);
|
|
break;
|
|
#ifdef WITH_ALTIVEC
|
|
case 16:
|
|
if (CURRENT_HOST_BYTE_ORDER != CURRENT_TARGET_BYTE_ORDER)
|
|
{
|
|
union { vreg v; unsigned_8 d[2]; } h, t;
|
|
memcpy(&t.v/*dest*/, buf/*src*/, description.size);
|
|
{ _SWAP_8(h.d[0] =, t.d[1]); }
|
|
{ _SWAP_8(h.d[1] =, t.d[0]); }
|
|
memcpy(cooked_buf/*dest*/, &h/*src*/, description.size);
|
|
break;
|
|
}
|
|
else
|
|
memcpy(cooked_buf/*dest*/, buf/*src*/, description.size);
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
memcpy(cooked_buf/*dest*/, buf/*src*/, description.size);
|
|
}
|
|
|
|
/* put the cooked value into the register */
|
|
switch (description.type) {
|
|
|
|
case reg_gpr:
|
|
cpu_registers(processor)->gpr[description.index] = *(gpreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_fpr:
|
|
cpu_registers(processor)->fpr[description.index] = *(fpreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_pc:
|
|
cpu_set_program_counter(processor, *(unsigned_word*)cooked_buf);
|
|
break;
|
|
|
|
case reg_spr:
|
|
cpu_registers(processor)->spr[description.index] = *(spreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_sr:
|
|
cpu_registers(processor)->sr[description.index] = *(sreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_cr:
|
|
cpu_registers(processor)->cr = *(creg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_msr:
|
|
cpu_registers(processor)->msr = *(msreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_fpscr:
|
|
cpu_registers(processor)->fpscr = *(fpscreg*)cooked_buf;
|
|
break;
|
|
|
|
#ifdef WITH_E500
|
|
case reg_gprh:
|
|
cpu_registers(processor)->e500.gprh[description.index] = *(gpreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_evr:
|
|
{
|
|
unsigned64 v;
|
|
v = *(unsigned64*)cooked_buf;
|
|
cpu_registers(processor)->e500.gprh[description.index] = v >> 32;
|
|
cpu_registers(processor)->gpr[description.index] = v;
|
|
break;
|
|
}
|
|
|
|
case reg_acc:
|
|
cpu_registers(processor)->e500.acc = *(accreg*)cooked_buf;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef WITH_ALTIVEC
|
|
case reg_vr:
|
|
cpu_registers(processor)->altivec.vr[description.index] = *(vreg*)cooked_buf;
|
|
break;
|
|
|
|
case reg_vscr:
|
|
cpu_registers(processor)->altivec.vscr = *(vscreg*)cooked_buf;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
printf_filtered("psim_write_register(processor=0x%lx,cooked_buf=0x%lx,reg=%s) %s\n",
|
|
(unsigned long)processor, (unsigned long)cooked_buf, reg,
|
|
"read of this register unimplemented");
|
|
break;
|
|
|
|
}
|
|
|
|
return description.size;
|
|
}
|
|
|
|
|
|
|
|
INLINE_PSIM\
|
|
(unsigned)
|
|
psim_read_memory(psim *system,
|
|
int which_cpu,
|
|
void *buffer,
|
|
unsigned_word vaddr,
|
|
unsigned nr_bytes)
|
|
{
|
|
cpu *processor;
|
|
if (which_cpu == MAX_NR_PROCESSORS) {
|
|
if (system->last_cpu == system->nr_cpus
|
|
|| system->last_cpu == -1)
|
|
which_cpu = 0;
|
|
else
|
|
which_cpu = system->last_cpu;
|
|
}
|
|
processor = system->processors[which_cpu];
|
|
return vm_data_map_read_buffer(cpu_data_map(processor),
|
|
buffer, vaddr, nr_bytes,
|
|
NULL, -1);
|
|
}
|
|
|
|
|
|
INLINE_PSIM\
|
|
(unsigned)
|
|
psim_write_memory(psim *system,
|
|
int which_cpu,
|
|
const void *buffer,
|
|
unsigned_word vaddr,
|
|
unsigned nr_bytes,
|
|
int violate_read_only_section)
|
|
{
|
|
cpu *processor;
|
|
if (which_cpu == MAX_NR_PROCESSORS) {
|
|
if (system->last_cpu == system->nr_cpus
|
|
|| system->last_cpu == -1)
|
|
which_cpu = 0;
|
|
else
|
|
which_cpu = system->last_cpu;
|
|
}
|
|
ASSERT(which_cpu >= 0 && which_cpu < system->nr_cpus);
|
|
processor = system->processors[which_cpu];
|
|
return vm_data_map_write_buffer(cpu_data_map(processor),
|
|
buffer, vaddr, nr_bytes, 1/*violate-read-only*/,
|
|
NULL, -1);
|
|
}
|
|
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_print_info(psim *system,
|
|
int verbose)
|
|
{
|
|
mon_print_info(system, system->monitor, verbose);
|
|
}
|
|
|
|
|
|
/* Merge a device tree and a device file. */
|
|
|
|
INLINE_PSIM\
|
|
(void)
|
|
psim_merge_device_file(device *root,
|
|
const char *file_name)
|
|
{
|
|
FILE *description;
|
|
int line_nr;
|
|
char device_path[1000];
|
|
device *current;
|
|
|
|
/* try opening the file */
|
|
description = fopen(file_name, "r");
|
|
if (description == NULL) {
|
|
perror(file_name);
|
|
error("Invalid file %s specified", file_name);
|
|
}
|
|
|
|
line_nr = 0;
|
|
current = root;
|
|
while (fgets(device_path, sizeof(device_path), description)) {
|
|
char *device;
|
|
/* check that the full line was read */
|
|
if (strchr(device_path, '\n') == NULL) {
|
|
fclose(description);
|
|
error("%s:%d: line to long - %s",
|
|
file_name, line_nr, device_path);
|
|
}
|
|
else
|
|
*strchr(device_path, '\n') = '\0';
|
|
line_nr++;
|
|
/* skip comments ("#" or ";") and blank lines lines */
|
|
for (device = device_path;
|
|
*device != '\0' && isspace(*device);
|
|
device++);
|
|
if (device[0] == '#'
|
|
|| device[0] == ';'
|
|
|| device[0] == '\0')
|
|
continue;
|
|
/* merge any appended lines */
|
|
while (device_path[strlen(device_path) - 1] == '\\') {
|
|
int curlen = strlen(device_path) - 1;
|
|
/* zap \ */
|
|
device_path[curlen] = '\0';
|
|
/* append the next line */
|
|
if (!fgets(device_path + curlen, sizeof(device_path) - curlen, description)) {
|
|
fclose(description);
|
|
error("%s:%s: unexpected eof in line continuation - %s",
|
|
file_name, line_nr, device_path);
|
|
}
|
|
if (strchr(device_path, '\n') == NULL) {
|
|
fclose(description);
|
|
error("%s:%d: line to long - %s",
|
|
file_name, line_nr, device_path);
|
|
}
|
|
else
|
|
*strchr(device_path, '\n') = '\0';
|
|
line_nr++;
|
|
}
|
|
/* parse this line */
|
|
current = tree_parse(current, "%s", device);
|
|
}
|
|
fclose(description);
|
|
}
|
|
|
|
|
|
#endif /* _PSIM_C_ */
|