diff options
Diffstat (limited to 'erts/etc/vxworks')
-rw-r--r-- | erts/etc/vxworks/README.VxWorks | 350 | ||||
-rw-r--r-- | erts/etc/vxworks/erl.exec.c | 129 | ||||
-rw-r--r-- | erts/etc/vxworks/erl_io.c | 108 | ||||
-rw-r--r-- | erts/etc/vxworks/erl_script.sam.in | 100 | ||||
-rw-r--r-- | erts/etc/vxworks/heart_config.c | 60 | ||||
-rw-r--r-- | erts/etc/vxworks/heart_config.h | 35 | ||||
-rw-r--r-- | erts/etc/vxworks/rdate.c | 87 | ||||
-rw-r--r-- | erts/etc/vxworks/reclaim.c | 551 | ||||
-rw-r--r-- | erts/etc/vxworks/reclaim.h | 150 | ||||
-rw-r--r-- | erts/etc/vxworks/reclaim_private.h | 44 | ||||
-rw-r--r-- | erts/etc/vxworks/resolv.conf | 6 | ||||
-rw-r--r-- | erts/etc/vxworks/vxcall.c | 145 | ||||
-rw-r--r-- | erts/etc/vxworks/wd_example.c | 141 |
13 files changed, 1906 insertions, 0 deletions
diff --git a/erts/etc/vxworks/README.VxWorks b/erts/etc/vxworks/README.VxWorks new file mode 100644 index 0000000000..299e35b513 --- /dev/null +++ b/erts/etc/vxworks/README.VxWorks @@ -0,0 +1,350 @@ + + %CopyrightBegin% + + Copyright Ericsson AB 1997-2009. All Rights Reserved. + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + %CopyrightEnd% + +----------------------------------------------------------------------- +README, Erlang/OTP R11B for VxWorks on PPC860 and PPC603 +----------------------------------------------------------------------- +20060515 -- Patrik Nyblom, [email protected] + +R11B is a libraries only release for VxWorks. Only the libraries of +erl_interface (ei+erl_inteface) and ic are expected to be used. Still +the whole erlang system is distributed, although no support will be +given for anything else but the libraries. The information in this +file still applies to the full erlang distribution and parts of it are +therefore somewhat irrelevant to commercial users. + + +Included OTP applications +------------------------- + +appmon +asn1 +compiler +cosEvent +cosNotification +cosTime +cosTransaction +debugger +erl_interface +erts +eva [1] +ic +inets [2] +jinterface +kernel +mesh +mnemosyne +mnesia [1] +mnesia_session +orber +os_mon +pman +runtime_tools +sasl +snmp +stdlib +tools +tv + +[1] Only ram_copies work, The VxWorks filesystems are not + reliable enough for disk_copies to be fully supported. +[2] CGI scripts do not work on VxWorks. + +Omitted applications +-------------------- + +crypto +emacs +etk +gs +odbc +parsetools +toolbar +ssl +megaco +webtools + +As `crypto' and `ssl' provides cryptographic functionality to `inets' +and `snmp', the latter applications will not handle cryptography on +VxWorks. + +Graphical interfaces +-------------------- + +For applications using graphical interfaces, only the backend part works. + +Compilers +--------- + +All compilers are expected to be run on a cross host. The VxWorks +systems memory capabilities are too restricting to allow native +compiling. The expected host system is a Sun Solaris machine, although +Erlang compilation may be done on most platforms. + +Supported boards and configuration (only libraries supported) +---------------------------------- +The following boards and configurations are supported: + +* Force PowerCore 603 with Force pcore603 BSP and VxWorks 3.5.1 (no + SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory. + +* Force Powercore 750 with Force pcore750 BSP and VxWorks 3.5.1 (no + SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory. + +* PSS Core PPC860 processors, only erl_interface (too small main memory). + +Most PowerPC boards with FPU are expected to work, but will need to be +tested by OTP to be fully supported. + +The PPC603 build has been compiled with Wind River's `-mlongcall' +flag (SPR25893) to support arbitrary function calls across more +than 32 MB of memory. + +The PPC860 (PowerQuicc) has no FPU and requires a separate build. + +For Erlang to run, the Wind kernel has to be configured with a minimum +of these variables defined in config.h (or by the Tornado +configuration tool): + + INCLUDE_ANSI_ALL + INCLUDE_ENV_VARS + INCLUDE_EXC_HANDLING + INCLUDE_EXC_TASK + INCLUDE_FLOATING_POINT + INCLUDE_FORMATTED_IO + INCLUDE_IO_SYSTEM + INCLUDE_LOADER + INCLUDE_NETWORK + INCLUDE_NET_INIT + INCLUDE_NET_SHOW + INCLUDE_NET_SYM_TBL or INCLUDE_STANDALONE_SYM_TBL + INCLUDE_PIPES + INCLUDE_POSIX_FTRUNC + INCLUDE_RLOGIN or INCLUDE_TELNET (for pty's only) + INCLUDE_SELECT + INCLUDE_SEM_BINARY + INCLUDE_SEM_COUNTING + INCLUDE_SEM_MUTEX + INCLUDE_SHELL (*) + INCLUDE_SHOW_ROUTINES + INCLUDE_SIGNALS + INCLUDE_STARTUP_SCRIPT (*) + INCLUDE_STDIO + INCLUDE_SYM_TBL + INCLUDE_TASK_HOOKS + INCLUDE_TASK_VARS + INCLUDE_TTY_DEV + INCLUDE_UNLOADER + INCLUDE_NFS or INCLUDE_RAMDRV or INCLUDE_DOSFS (i.e. a file system, + possibly read-only) (**) + +(*) Needed for the example startup script, not actually needed in production if + erlang is set up by a c routine called from usrConfig.c. +(**) INCLUDE_NFS usually requires the NFS_USER_ID and NFS_GROUP_ID variables + to be set in config.h + +As an erlang system may open a lot of files, it is recommended to raise the +default NUM_FILES variable to something like 256 in config.h like this: + #ifdef NUM_FILES + #undef NUM_FILES + #endif + #define NUM_FILES 256 + +The SENS stack *has* to be of version 1.1 or higher, 1.0 is *not* +supported and will not work reliably. Upgrades as well as the patch +for SPR23938 can be found at www.wrs.com (i.e. WindSurf). Also, the +following constants in $WIND_BASE/target/h/netBufLib.h has to be +raised to a value of at least four times the default: + + NUM_NET_MBLKS + NUM_64 + NUM_128 + NUM_256 + NUM_512 + NUM_1024 + NUM_2048 + + NUM_SYS_64 + NUM_SYS_128 + NUM_SYS_256 + NUM_SYS_512 + +Use the show routines mbufShow and netStackSysPoolShow to verify that +these pools are not exhausted. + +Installation +------------ + +To install Erlang on a VxWorks card, the following knowledge is +expected: + +* VxWorks installation and configuration. + +* Network (TCP/IP) configuration. + +* Erlang basic operation and configuration. + +There is no specific install script for erlang on the VxWorks +platform. There is however an example VxWorks startup file named +erts-5.0.1/bin/erl_script.sam under the root of an unpacked +release. There may of course be other things to do in the start +script, like using the rdate program in the erlang distribution to get +a correct date and set the TIMEZONE variable. + +Please consult the "Embedded System" documentation for further +information on installation. + +Known Bugs and problems +----------------------- + +We have found the VxWorks/NFS client file system to be unreliable. +Important file operations like rename, ftruncate, cd and unlink +doesn't always work as expected. Therefore more complex file using +parts of OTP, like DETS and disk based mnesia tables cannot be used +reliably with NFS. Lowering the NFS cache size (global variable +nfsCacheSize) to 512 gives a more reliable NFS client, but to disk +base the mnesia tables over NFS is still not a good idea, especially +as disk based mnesia tables are not supported on VxWorks. Another +problem with VxWorks file systems is that the error codes they produce +are not consistent. We have worked around this problem by mapping the +codes to POSIX ones, by doing this we make the VxWorks Erlang platform +behave similarly to the UNIX and Windows implementations. + +The rename and ftruncate operations over NFS are emulated using +copying of files. This is mainly for our own test suites and it is not +recommended to use file:rename and/or file:ftruncate on NFS file +systems in production. + +Floating point operations is somewhat faulty. For instance, testing +floating point numbers for equality should be done with care. This is +actually not a bug, IEEE specifies no equality among floating point +numbers. + +Memory handling +--------------- + +Please read the erl_set_memory_block(3) manual page in the ERTS +documentation for information concerning memory handling in the erlang +emulator. Also please observe that reclaim.o has to be loaded and +reclaim_init() called before any other erlang routines are loaded and +started. If one wish to use the resource reclamation routines in other +programs, refer to the header file in `erts-5.0.1/include/reclaim.h'. +Including that file in your C source makes malloc/realloc/free and +open/fopen/socket/close etc be redefined to routines that makes the +memory and files be free'd/closed when the task exits. Still, +reclaim_init() *has* to be called before any program that uses this is +started. + +Using heart +----------- + +The default behavior of the heart object file that is part of the +distribution is that it reboots the target when the Erlang process +hasn't given it a heart beat in 60 seconds. The normal heart beat rate +is one beat per five seconds. This makes an effective "software +watchdog" but there is really no substitute for the real thing --- a +hardware watchdog. If you want to add a hardware watchdog to the +system please contact us for directions. If you want to disable the +reboot you may set the environment variable HEART_DONT_REBOOT (see the +example erlang start script, erl). Please note that if you DO want the +card to reboot you mustn't define HEART_DONT_REBOOT at all. E.g. to +disable heart reboot you may include the following line in the start +script (as is indeed the case with the example start script). + + putenv "HEART_DONT_REBOOT=1" + +A few words on writing port program and dynamically loaded drivers for VxWorks +------------------------------------------------------------------------------ + +VxWorks has one name-space for all symbols. This makes it harder to +write C programs whose global symbols doesn't interfere with each +other. It is a good rule to avoid all globally visible symbols that +are not absolutely necessary. Due to these facts we use the following +naming rules that are crucial to follow. (there are more issues +involved but the issues described here is a good beginning). + +Port programs must have a function with the same name as the object +file. E.g. if you have an object file named `port_test.o' it must +contain a globally visible function named `port_test'. This is the +function that will be called when you output data from Erlang to the +port. (The object file, in this example, should be named +`port_test.o', but `port_test' will also do). + +Also, in an embedded system, it is recommended to load the port +program into the system before the port program is used. This is to +avoid the real time degradation dynamical linking in runtime would +introduce. Use VxWorks command ld < "port_prg" to accomplish this. + +Dynamically linked drivers must have a function with the same name as +the object file with the added suffix `_init'. We recommend the use of +the macro DRIVER_INIT in `driver.h'. E.g. if you have an object file +named `echo_drv.eld' it must contain a globally visible function +`echo_drv_init'. (The object file, in this example, should be named +`echo_drv.eld' (`eld' is short for Erlang Loadable Driver), but +`echo_drv.o' and `echo_drv' will both also do). It is also very +important to initialize all unused function pointer in the +`driver_entry' struct to NULL (see example below). + +Example of dynamically linked driver +------------------------------------ + +#include <stdio.h> +#include "driver.h" + +static int erlang_port; +static long echo_start(); +static int echo_stop(), echo_read(); + +static struct driver_entry echo_driver_entry = { + null_func, + echo_start, + echo_stop, + echo_read, + null_func, + null_func, + "echo_drv", + null_func +}; + +int DRIVER_INIT(echo_drv)(void *handle) +{ + erlang_port = -1; + + echo_driver_entry.handle = handle; + return (int) &echo_driver_entry; +} + +static long echo_start(long port,char *buf) +{ + if (erlang_port != -1) { + return -1; + } + + erlang_port = port; + return port; +} + +static int echo_read(long port, char *buf, int count) +{ + return driver_output(erlang_port, buf, count); +} + +static int echo_stop() +{ + return erlang_port = -1; +} diff --git a/erts/etc/vxworks/erl.exec.c b/erts/etc/vxworks/erl.exec.c new file mode 100644 index 0000000000..6b45ebaa39 --- /dev/null +++ b/erts/etc/vxworks/erl.exec.c @@ -0,0 +1,129 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + A simpified version of the 'erl.exec' "startup script". + Called (e.g. from VxWorks shell) with all arguments in a + single string, e.g.: erl "-name thisnode -s mymod myfunc". + These arguments are handled as in 'erl.exec': + -name + -sname + -noshell + -noinput + anything else is just passed on to the emulator. Note that there + is no automatic start of epmd, that -oldshell is implicit, and + that you need to set current directory appropriately if you want + auto-load of port programs +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DEFAULT_HOMEDIR /* used if environment HOME isn't set */ +#define DEFAULT_HOMEDIR "/" +#endif + +#define ARGLEN 2048 /* Total length of args passed to erl_main */ +#define ARGMAX 64 /* Max no of "extra" args */ + +static char *erl_cmd = "erl_main -n "; + +static toomuch() +{ + fprintf(stderr, "erl: Too many arguments\n"); + return(-1); +} + +static toolittle(arg) +char *arg; +{ + fprintf(stderr, "erl.exec: Missing argument for %s\n", arg); + return(-1); +} + +erl_exec(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) +int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10; +{ + char *shell = "-oldshell ", *noshell = "", + *home, *rootdir, *bindir, *progname; + char cmd[ARGLEN], eargs[ARGLEN], iargs[ARGLEN]; + char *args[ARGMAX], *arglast = NULL, *argp; + int nargs = 0, len, i; + + if ((rootdir = getenv("ROOTDIR")) == NULL || + (bindir = getenv("BINDIR")) == NULL || + (progname = getenv("PROGNAME")) == NULL) { + fprintf(stderr, "erl.exec: ROOTDIR, BINDIR, and PROGNAME must be set."); + return -1; + } + eargs[0] = '\0'; + iargs[0] = '\0'; + if ((home = getenv("HOME")) == NULL) + home = DEFAULT_HOMEDIR; + argp = strtok_r((char *)arg1, " \t", &arglast); + while (argp != NULL) { + if (strcmp(argp, "-name") == 0) { + if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL) + return(toolittle("-name")); + strcat(iargs, "-name "); + strcat(iargs, argp); + strcat(iargs, " "); + } else if (strcmp(argp, "-sname") == 0) { + if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL) + return(toolittle("-sname")); + strcat(iargs, "-sname "); + strcat(iargs, argp); + strcat(iargs, " "); + } else if (strcmp(argp, "-noshell") == 0) { + strcat(iargs, "-noshell -noinp_shell "); + } else if (strcmp(argp, "-noinput") == 0) { + strcat(iargs, "-noshell -noinput "); + } else { + if (nargs > ARGMAX - 1) + return(toomuch()); + args[nargs++] = argp; + } + argp = strtok_r((char *)NULL, " \t", &arglast); + } + strcpy(cmd, erl_cmd); + strcat(cmd, eargs); + strcat(cmd, " -- -root "); + strcat(cmd, rootdir); + strcat(cmd, " -progname "); + strcat(cmd, progname); + strcat(cmd, " -- "); + strcat(cmd, "-home "); + strcat(cmd, home); + strcat(cmd, " "); + strcat(cmd, iargs); + + len = strlen(cmd); + for (i = 0; i < nargs; i++) { + if (len + strlen(args[i]) + 2 >= ARGLEN) + return(toomuch()); + cmd[len++] = ' '; + strcpy(&cmd[len], args[i]); + len += strlen(args[i]); + } + argcall(cmd); +} + diff --git a/erts/etc/vxworks/erl_io.c b/erts/etc/vxworks/erl_io.c new file mode 100644 index 0000000000..0032b77079 --- /dev/null +++ b/erts/etc/vxworks/erl_io.c @@ -0,0 +1,108 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* Some stuff to let the Erlang and VxWorks shells coexist peacefully. + Basically, run Erlang as a spawned task with input redirected to + the slave side of a pseudo-tty, and connect explicitly to the master + side of the pseudo-tty to send input to Erlang when desired. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#include <ioLib.h> +#include <taskLib.h> +#include <ptyDrv.h> + +extern int spTaskPriority, spTaskOptions; + +#define TBUFSIZ 512 + +#define DEFAULT_STACK_SIZE 100000 + +static int slavefd = -1, masterfd = -1; +static run_erl(); + +/* Frontend to the Erlang startup function - callable from VxWorks shell + or script. 'arg' is actually a string passed to the real startup. */ +start_erl(arg) +int arg; +{ + int stacksize; + char *stackenv; + + /* create and open the pty - we want the master side to be open + all the time, since closing it probably generates EOF on the + slave side */ + (void)ptyDevCreate("/pty/erlang.", TBUFSIZ, TBUFSIZ); + if (slavefd != -1) + (void)close(slavefd); + slavefd = open("/pty/erlang.S", O_RDONLY, 0); + if (masterfd != -1) + (void)close(masterfd); + masterfd = open("/pty/erlang.M", O_WRONLY, 0); + + /* flush any old leftover garbage */ + (void) ioctl(masterfd, FIOFLUSH, 0); + if ((stackenv = getenv("ERLSTACKSIZE")) == NULL) + stacksize = DEFAULT_STACK_SIZE; + else + stacksize = atoi(stackenv); + /* spawn Erlang, via stub below */ + return(taskSpawn("erlang", spTaskPriority, spTaskOptions, stacksize, + run_erl, arg, 0,0,0,0,0,0,0,0,0)); +} + +/* Little stub that runs in the spawned task - we need this to redirect + stdin reliably (redirections aren't "inherited" in VxWorks) */ +static +run_erl(arg) +int arg; +{ + ioTaskStdSet(0, 0, slavefd); /* redirect stdin to slave side of pty */ + + /* We don't want to redirect stdout/err since no one will be reading + from the master side (to_erl - and the open()s above - could be + made bidirectional, but still the master side would only be read + when to_erl was running), and output can eventually fill the pty + buffer and cause the Erlang system to block. Not redirecting + stdout/err will have the effect that output from Erlang, e.g. the + shell prompt, will appear on console/rlogin/whatever even when + to_erl isn't running, which may be confusing - can't win 'em all... */ + + erl_exec(arg, 0,0,0,0,0,0,0,0); /* call the real startup */ +} + +/* Function callable from VxWorks shell to talk to Erlang - stop talking + and return to VxWorks shell through ^D (EOF) */ +to_erl() +{ + char buf[TBUFSIZ]; + int cc; + + if (masterfd == -1) { /* sanity check */ + fprintf(stderr, "Must start_erl first!\n"); + return(-1); + } + while ((cc = read(0, buf, TBUFSIZ)) > 0) /* just pass everything through */ + if (write(masterfd, buf, cc) != cc) { + fprintf(stderr, "Write to Erlang failed!\n"); + return(-1); + } + return(cc); +} diff --git a/erts/etc/vxworks/erl_script.sam.in b/erts/etc/vxworks/erl_script.sam.in new file mode 100644 index 0000000000..81c2b0128d --- /dev/null +++ b/erts/etc/vxworks/erl_script.sam.in @@ -0,0 +1,100 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +# erl_script.sam +# Sample VxWorks script to start Erlang +# +# Note! This is not a complete or ready to use VxWorks startup script, +# rather an example of what You should add to Your existing startupscript +# to execute the erlang emulator at boot. +# +# When writing Your own script to start erlang, the paths to +# the binaries have to be changed to reflect your system. +# +# The ROOTDIR variable should not point to a ftp or rcp filesystem unless +# the erlang machine is run in embedded mode. Loading of modules +# is far to slow if the erlang binaries are not placed on a real filesystem +# like NFS or any type of local filesystem. +# + +# +# Load modules +# + +# +# First load and initiate the reclaim facility +# +ld </home/tornado/erlvxworks/erts-%VSN%/bin/reclaim.o +reclaim_init() + +# +# Now load the runtime system +# +ld </home/tornado/erlvxworks/erts-%VSN%/bin/jam +ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl.exec +ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl_io +ld </home/tornado/erlvxworks/erts-%VSN%/bin/vxcall +ld </home/tornado/erlvxworks/erts-%VSN%/bin/heart +ld </home/tornado/erlvxworks/erts-%VSN%/bin/epmd + +# +# Stack sizes +# +putenv "ERLSTACKSIZE=100000" +putenv "ERLPORTSTACKSIZE=100000" + +# +# Activate Round robin scheduling +# +kernelTimeSlice 1 + +# +# Distribution +# The VxWorks machines host name +sethostname "sb001", 5 +# Erlangs internal resolver +putenv "ERLRESCONF=/home/tornado/erlvxworks/erts-%VSN%/bin/resolv.conf" + +# +# Start epmd (for distribution) +# +start_epmd "-daemon" + +# +# Misc environment variables +# +putenv "ROOTDIR=/home/tornado/erlvxworks" +putenv "BINDIR=/home/tornado/erlvxworks/erts-%VSN%/bin" +putenv "PROGNAME=erl" +putenv "HOME=/home/tornado/erlvxworks" + +# +# Set heart no reboot mode (to make heart reboot - +# don't define HEART_DONT_REBOOT at all) +# +putenv "HEART_DONT_REBOOT=1" + +# To get fullsweep garbage collection on systems with +# very limited memory, set ERL_FULLSWEEP_AFTER to "0": +# putenv "ERL_FULLSWEEP_AFTER=0" + +# +# Start Erlang/OTP +# +start_erl "-oldshell -heart -sname vxnode -setcookie switch -boot start_sasl" diff --git a/erts/etc/vxworks/heart_config.c b/erts/etc/vxworks/heart_config.c new file mode 100644 index 0000000000..7e60e61fbb --- /dev/null +++ b/erts/etc/vxworks/heart_config.c @@ -0,0 +1,60 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * A basic heart configure module for VxWorks. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <vxWorks.h> +#include <stdio.h> +#include <stdlib.h> +#include <rebootLib.h> +#include <sysLib.h> + +/* wd_init is executed to initialize a watchdog (if one is used). */ +int wd_init(timeout, prio) + int timeout, prio; +{ + +} + +/* wd_reset should be called every 5th second from heart */ +void wd_reset() +{ + +} + +/* This routine is called when erlang has closed */ +void heart_reboot() +{ + if (getenv("HEART_DONT_REBOOT") != NULL) { + fprintf(stderr, "heart_config: HEART_DONT_REBOOT set, no reboot ...\n"); + } else { + fprintf(stderr, "heart_config: rebooting ...\n"); + taskDelay(sysClkRateGet() * 5); + reboot(BOOT_CLEAR); + } +} + + + + diff --git a/erts/etc/vxworks/heart_config.h b/erts/etc/vxworks/heart_config.h new file mode 100644 index 0000000000..5ffaaa8c3f --- /dev/null +++ b/erts/etc/vxworks/heart_config.h @@ -0,0 +1,35 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * This is heart's watchdog interface for VxWorks. + */ + +#ifndef _HW_WATCHDOG_H +#define _HW_WATCHDOG_H + +extern void wd_init(int timeout, int prio); /* wd_init initializes the + watchdog, if one is used. */ +extern void wd_reset(void); /* wd_reset is used by heart to kick + the watchdog, if one is used. */ +extern void heart_reboot(void); /* reboot is called if heart discovers + that the Erlang task has stopped sending + heart beats. It can log system status + and should reboot VxWorks. */ + +#endif diff --git a/erts/etc/vxworks/rdate.c b/erts/etc/vxworks/rdate.c new file mode 100644 index 0000000000..3e8cc644d0 --- /dev/null +++ b/erts/etc/vxworks/rdate.c @@ -0,0 +1,87 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <vxWorks.h> +#include <timers.h> +#ifdef NETDB +#include <netdb.h> +#endif +#include <sys/socket.h> +#include <netinet/in.h> + +/* + rdate("host") - Set the time from "host". +*/ + +/* No getservbyname() available... */ +#define TIME_PORT 37 + +rdate(host) +char *host; +{ + u_long haddr; +#ifdef NETDB + struct hostent *hp; +#endif + struct sockaddr_in saddr; + int sock; + u_long net_time; + struct timespec t; + + if ((haddr = inet_addr(host)) == ERROR) { +#ifdef NETDB + if ((hp = gethostbyname(host)) == NULL) { +#else + if ((haddr = hostGetByName(host)) == ERROR) { +#endif + printf("Host not found.\n"); + return(-1); + } +#ifdef NETDB + memcpy(&haddr, hp->h_addr, sizeof(haddr)); +#endif + } + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + memcpy(&saddr.sin_addr, &haddr, sizeof(haddr)); + saddr.sin_port = htons(TIME_PORT); + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("socket"); + return(-1); + } + if (connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + perror("connect"); + close(sock); + return(-1); + } + if (read(sock, &net_time, 4) != 4) { + perror("read"); + close(sock); + return(-1); + } + t.tv_sec = ntohl(net_time); + t.tv_sec -= 2208988800; /* seconds 1900-01-01 -- 1970-01-01 */ + t.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &t); + close(sock); + return(0); +} diff --git a/erts/etc/vxworks/reclaim.c b/erts/etc/vxworks/reclaim.c new file mode 100644 index 0000000000..d8676b3750 --- /dev/null +++ b/erts/etc/vxworks/reclaim.c @@ -0,0 +1,551 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <vxWorks.h> +#include <version.h> +#include <string.h> +#include <types.h> +#include <sigLib.h> +#include <ioLib.h> +#include <iosLib.h> +#include <fioLib.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <symLib.h> +#include <sysLib.h> +#include <sysSymTbl.h> +#include <loadLib.h> +#include <taskLib.h> +#include <taskVarLib.h> +#include <taskHookLib.h> +#include <tickLib.h> +#include <time.h> +#include <rngLib.h> +#include <semLib.h> +#include <selectLib.h> +#include <sockLib.h> +#include <a_out.h> +#include <wdLib.h> +#include <timers.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <stdarg.h> + +#include <stdio.h> +#include <math.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + + + +#define RECLAIM_NO_ALIAS /* No #defines for open/malloc/fopen etc */ +#include "reclaim.h" +#include "reclaim_private.h" + +#undef open +#undef creat +#undef socket +#undef accept +#undef close +#undef fopen +#undef fdopen +#undef freopen +#undef fclose +/* XXX Should do opendir/closedir too... */ +#undef malloc +#undef calloc +#undef realloc +#undef free +#undef cfree + +#ifdef _ARCH_PPC +#define MAX_FILES_SYM_NAME "maxFiles" +#else +#define MAX_FILES_SYM_NAME "_maxFiles" +#endif + + +/* + * Use another free() function upon task deletion? + * Note! When changing free function, the new free function will have to + * be able to cope with ALL previously used malloced areas, that is + * it has to be able to find out how things are malloced + * to free them in the right way! + */ +static FreeFunction reclaim_free_function = NULL; + +/* delete hook handling (see below) */ +static int hook_added = 0; /* Initated at first reclaim_init, an extra + non MT-safe check that we only get + initialized once */ + +/* Forward... */ +static void save_reclaim(WIND_TCB *tcbp); + +struct mall_data { + struct mall_data *next; + char *self; +}; + +struct task_data { + FUNCPTR version; /* To recognize when we have reloaded */ + int max_files; /* It may change... */ + struct fd_set open_fds; + struct mall_data *mall_data; + FUNCPTR delete_hook; + caddr_t hook_data; + FILE *open_fps[1]; /* Will be max_files long */ +} *task_data = NULL; + +static int max_files = 50; /* default configAll.h */ + +int reclaim_max_files(void) +{ + return max_files; +} + +#ifdef DEBUG +#define check_hook() \ +((task_data != NULL || \ + fdprintf(2,"check_hook() TID = 0x%08x, Called from line %d\n", \ + (unsigned int)taskIdSelf(),\ + __LINE__)) && \ +(task_data != NULL || \ + (taskVarAdd(0, (int *)&task_data) == OK && \ + (task_data = (struct task_data *)\ + calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \ + (task_data->version = (FUNCPTR)save_reclaim) != NULL && \ + (task_data->max_files = max_files) != 0 && \ + fdprintf(2,"taskVar Added for 0x%08x\n",(unsigned int)taskIdSelf())))) +#else +#define check_hook() \ +(task_data != NULL || \ + (taskVarAdd(0, (int *)&task_data) == OK && \ + (task_data = (struct task_data *)\ + calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \ + (task_data->version = (FUNCPTR)save_reclaim) != NULL && \ + (task_data->max_files = max_files) != 0)) +#endif + +/* + * Global initialization of the reclaim data structures, mainly + * the max_files variable. This HAS to be called by some task before + * the first task that utilizes this exit's, preferrably before any + * task makes the first use of this library. + */ +STATUS reclaim_init(void) +{ + int *mp; + SYM_TYPE type; + struct task_data *tdp; + int i; + + if (!hook_added) { + /* race condition */ + ++hook_added; + /* Try to find the maxFiles value */ + if (symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp, + &type, + N_EXT | N_BSS, N_EXT | N_BSS) == OK || + symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp, + &type, + N_EXT | N_DATA, N_EXT | N_DATA) == OK) { + +#ifdef DEBUG + fdprintf(2, "Found maxFiles=%d\n", *mp); +#endif + if (*mp <= FD_SETSIZE) + max_files = *mp; + else + max_files = FD_SETSIZE; + } + if (task_data != NULL && task_data->max_files != max_files) { + /* fix our own iff we have one */ + if ((tdp = (struct task_data *) + realloc(task_data, sizeof(struct task_data) + + max_files*sizeof(FILE *))) != NULL) { + task_data = tdp; + for (i = task_data->max_files; i < max_files; i++) + task_data->open_fps[i] = NULL; + task_data->max_files = max_files; + } + } + /* Make sure taskVariables are deleted AFTER our hook is run. */ + taskVarInit(); + if(taskDeleteHookAdd((FUNCPTR)save_reclaim) != OK) { + fprintf(stderr, + "Panic: taskDeleteHook cannot be added for reclaim.\n"); + return ERROR; + } + return OK; + } else + return ERROR; +} + +/* N.B.!! We do *not* execute in the context of the dying task here, + but rather that of tExcTask - we do get a pointer to the task's + TCB though - this pointer is in fact also the task's ID. */ +static void save_reclaim(WIND_TCB *tcbp) +{ + int i, var, oldfd; + struct task_data *tdp; + struct mall_data *mdp, *mdnextp; + + if ((var = taskVarGet((int)tcbp, (int *)&task_data)) != ERROR && + var != 0) { + tdp = (struct task_data *)var; + if (tdp->version == (FUNCPTR)save_reclaim) { /* Only handle our own */ +#ifdef DEBUG + fdprintf(2, "Reclaiming for task id 0x%x:\nFiles: ", (int)tcbp); +#endif + /* Ugh! VxWorks doesn't even flush stdout/err - we need to + get at those (which are task-private of course, i.e. we + can't just do fflush(stdout) here) - we could be really + pedantic and try to redefine stdout/err (which "are" + function calls) too, snarfing the values when they are + used - but besides the overhead this is problematic since + they are actually #defines already... We'll peek in the + TCB instead (no documentation of course). And of course, + we must also meddle with the *file descriptor* indirections, + or we'll just flush out on tExcTask's descriptors... */ + for (i = 1; i <= 2; i++) { + if (tcbp->taskStdFp[i] != NULL) { +#ifdef DEBUG + fdprintf(2, "fflush(%s) ", i == 1 ? "stdout" : "stderr"); +#endif + oldfd = ioTaskStdGet(0, i); + ioTaskStdSet(0, i, tcbp->taskStd[i]); + fflush(tcbp->taskStdFp[i]); + ioTaskStdSet(0, i, oldfd); + } + } + for (i = 3; i < tdp->max_files; i++) { + if (FD_ISSET(i, &tdp->open_fds)) { +#ifdef DEBUG + fdprintf(2, "close(%d) ", i); +#endif + (void) close(i); + } + if (tdp->open_fps[i] != NULL) { +#ifdef DEBUG + fdprintf(2, "fclose(%0x%x) ", (int)tdp->open_fps[i]); +#endif + (void) fclose(tdp->open_fps[i]); + } + } + i = 0; + mdp = tdp->mall_data; + while (mdp != NULL) { + mdnextp = mdp->next; + if(reclaim_free_function != NULL) + (*reclaim_free_function)(mdp->self); + else + free(mdp->self); + i++; + mdp = mdnextp; + } +#ifdef DEBUG + fdprintf(2, "\nFreeing memory: total %d mallocs\n", i); +#endif + + if (tdp->delete_hook != NULL) { +#ifdef DEBUG + fdprintf(2, "Calling delete hook at 0x%08x\n", tdp->delete_hook); +#endif + (*tdp->delete_hook)(tdp->hook_data); +#ifdef DEBUG + fdprintf(2, "Called delete hook at 0x%08x\n", tdp->delete_hook); +#endif + } +#ifdef DEBUG + fdprintf(2, "Freeing own mem at 0x%08x\n", tdp); +#endif + (void) free((char *)tdp); +#ifdef DEBUG + fdprintf(2, "Freed own mem at 0x%08x, done (0x%08x)\n**********\n", tdp, + taskIdSelf()); + checkStack(0); +#endif + } + } +#ifdef DEBUG + else + fdprintf(2, "No task data found for id 0x%x, var = %d\n", (int)tcbp, var); +#endif +} + +/* + * This sets another free function to be used by the task deletion hook. + * The free function HAS to be able to free ANY type of dynamically allocated + * memory that can be in the task data list of allocated memory, that is + * also memory that's allocated before the free function was set. + * A "user-supplied" free function is GLOBAL to the system!!! + * A race condition is present, a task delete hook may be running when this + * function is called, that may not be especially funny... + */ +void set_reclaim_free_function(FreeFunction f){ + reclaim_free_function = f; +} + +void save_delete_hook(FUNCPTR func, caddr_t parm) +{ + if (check_hook()) { + task_data->delete_hook = func; + task_data->hook_data = parm; + } +} + +/* + * plain_malloc is only used by spawn_start; plain_free by call_proc; + * save_fd is used by both. + */ +void *plain_malloc(size_t size){ + return(malloc(size)); +} + +void *plain_realloc(void *ptr, size_t size){ + return(realloc(ptr, size)); +} + +void plain_free(void *ptr){ + free(ptr); +} + +void save_fd(int fd){ + if (fd >= 0 && check_hook() && fd < task_data->max_files) + FD_SET(fd, &task_data->open_fds); +} + +int save_open(char *path, int flags, /*mode_t mode*/ ...){ + int fd; + mode_t mode = 0; + if(flags & O_CREAT){ + va_list pvar; + va_start(pvar,flags); +#ifdef __GNUC__ +#warning save_open() gives three known alignment warnings. +#endif + mode = va_arg(pvar, mode_t); + va_end(pvar); + } + if ((fd = open(path, flags, mode)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_creat(char *path, int mode){ + int fd; + if ((fd = creat(path, mode)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_socket(int domain, int type, int protocol){ + int fd; + if ((fd = socket(domain, type, protocol)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_accept(int s, struct sockaddr *addr, int *addrlen){ + int fd; + if ((fd = accept(s, addr, addrlen)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_close(int fd){ + if (fd >= 0 && fd <= FD_SETSIZE && check_hook()) + FD_CLR(fd, &task_data->open_fds); + return(close(fd)); +} + +/* The dealing with FILE *'s below isn't strictly correct, we assume + that one never has several pointing to the same fd - in the unlikely + event that one does, all but the last one opened is forgotten */ +FILE *save_fopen(const char *filename, char *type){ + FILE *fp; + + if ((fp = fopen(filename, type)) != NULL && + check_hook() && fileno(fp) < task_data->max_files) + task_data->open_fps[fileno(fp)] = fp; + return(fp); +} + +FILE *save_fdopen(int fd, char *type){ + FILE *fp; + + if ((fp = fdopen(fd, type)) != NULL && + check_hook() && fileno(fp) < task_data->max_files) { + task_data->open_fps[fileno(fp)] = fp; + FD_CLR(fd, &task_data->open_fds); + } + return(fp); +} + +FILE *save_freopen(char *filename, char *type, FILE *stream){ + FILE *fp; + + if (check_hook()) { + if(fileno(stream) < task_data->max_files && + task_data->open_fps[fileno(stream)] == stream) + task_data->open_fps[fileno(stream)] = NULL; + if ((fp = freopen(filename, type, stream)) != NULL && + fileno(fp) < task_data->max_files) + task_data->open_fps[fileno(fp)] = fp; + } else + fp = freopen(filename, type, stream); + return(fp); +} + +int save_fclose(FILE *stream){ + if (check_hook() && fileno(stream) < task_data->max_files && + task_data->open_fps[fileno(stream)] == stream) + task_data->open_fps[fileno(stream)] = NULL; + return(fclose(stream)); +} + +/* We link all malloc'ed segments by adding a couple of pointers + at the *end* - that way we can return the address malloc gave + (need to make sure we align those pointers) */ + +/* + #define MALL_MARGIN 32 + #define save_mall_size(size) save_mall_size1((size) + 2 * MALL_MARGIN) + #define save_mall_size1(size) \ + (((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*))) + + #define save_mall_enq(ptr, mdp) save_mall_enq1((ptr), (mdp) - MALL_MARGIN) + #define save_mall_enq1(ptr, mdp) \ + (((struct mall_data *)(mdp))->self = (ptr), \ + ((struct mall_data *)(mdp))->next = task_data->mall_data, \ + task_data->mall_data = (struct mall_data *)(mdp)) +*/ +#define save_mall_size(size) \ +(((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*))) +#define save_mall_enq(ptr, mdp) \ +(((struct mall_data *)(mdp))->self = (ptr), \ + ((struct mall_data *)(mdp))->next = task_data->mall_data, \ + task_data->mall_data = (struct mall_data *)(mdp)) + + +#define save_mall_deq(ptr) { \ + struct mall_data *mdp = task_data->mall_data, \ + **prevnext = &task_data->mall_data; \ + while (mdp != NULL && mdp->self != (ptr)) { \ + prevnext = &mdp->next; \ + mdp = mdp->next; \ + } \ + if (mdp != NULL) *prevnext = mdp->next; \ +} + +void *save_malloc2(size_t size, MallocFunction mf){ + unsigned msize = save_mall_size(size); + char *ptr; + + if ((ptr = (*mf)(msize + sizeof(struct mall_data))) != NULL && + check_hook()) + save_mall_enq((void *) ptr, (void *) (ptr + msize)); + return((void *) ptr); +} + +void *save_malloc(size_t size){ + return save_malloc2(size, &malloc); +} + +void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf){ + unsigned msize = save_mall_size(nelem * elsize); + char *ptr; + + if ((ptr = (*cf)(1, msize + sizeof(struct mall_data))) != NULL && + check_hook()) + save_mall_enq((void *) ptr, (void *) (ptr + msize)); + return((void *) ptr); +} + +void *save_calloc(size_t nelem, size_t elsize){ + return save_calloc2(nelem,elsize,&calloc); +} + +void *save_realloc2(void *optr, size_t size, ReallocFunction rf){ + unsigned msize = save_mall_size(size); + char *ptr; + + /* First we must dequeue the old save block, after that + we try to realloc, if that succeeds we enqueue the new + block, if it fails we have to enqueue the old one anew + so we must deduce the size of that old block first. */ + + struct mall_data *mdp0 = task_data->mall_data, + **prevnext0 = &task_data->mall_data; + while (mdp0 != NULL && mdp0->self != (((char *) optr))) { + prevnext0 = &mdp0->next; + mdp0 = mdp0->next; + } + /* mdp0 == NULL (can) mean that the block that is realloced + have been malloced with an (for example) ordinary malloc + (that is not a save_malloc). This is handled like: no dequeing + is done of that block, the new block is enqueued */ + if (mdp0 != NULL) + save_mall_deq(((char *) optr)); + + if ((ptr = (*rf)(optr, msize + sizeof(struct mall_data))) != NULL && + check_hook()) + save_mall_enq((void *) ptr, (void *) (ptr + msize)); + else if (mdp0 != NULL) + /* re-enqueue the old block that has just been dequeued */ + save_mall_enq(((char *) optr), mdp0); + + return((void *) ptr); +} + +void *save_realloc(void *optr, size_t size){ + return save_realloc2(optr,size,&realloc); +} + +void save_free2(void *ptr, FreeFunction ff) +{ + if (check_hook()) + save_mall_deq(((char *) ptr)); + (*ff)(ptr); +} + +void save_free(void *ptr){ + save_free2(ptr,&free); +} + +void save_cfree2(void *ptr, CfreeFunction cf) +{ + if (check_hook()) + save_mall_deq(((char *)ptr)); + (*cf)(ptr); +} + +void save_cfree(void *ptr){ + save_cfree2(ptr,&cfree); +} + diff --git a/erts/etc/vxworks/reclaim.h b/erts/etc/vxworks/reclaim.h new file mode 100644 index 0000000000..ca9aa8f6be --- /dev/null +++ b/erts/etc/vxworks/reclaim.h @@ -0,0 +1,150 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#ifndef _RECLAIM_H +#define _RECLAIM_H + + +/* The Erlang release for VxWorks includes a simple mechanism for + "resource reclamation" at task exit - it allows replacement of the + functions that open/close "files" and malloc/free memory with versions + that keep track, to be able to "reclaim" file descriptors and memory + when a task exits (regardless of *how* it exits). + + The interface to this mechanism is made available via this file, + with the following caveats: + + - The interface may change (or perhaps even be removed, though that + isn't likely until VxWorks itself provides similar functionality) + in future releases - i.e. you must always use the version of this + file that comes with the Erlang release you are using. + + - Disaster is guaranteed if you use the mechanism incorrectly (see + below for the correct way), e.g. allocate memory with the "tracking" + version of malloc() and free it with the "standard" version of free(). + + - The mechanism (of course) incurs some performance penalty - thus + for a simple program you may be better off with careful programming, + making sure that you do whatever close()/free()/etc calls that are + appropriate at all exit points (though if you need to guard against + taskDelete() etc, things get messy...). + + To use the mechanism, simply program your application normally, i.e. + use open()/close()/malloc()/free() etc as usual, but #include this + file before any usage of the relevant functions. NOTE: To avoid the + "disaster" mentioned above, you *must* #include it in *all* (or none) + of the files that manipulate a particular file descriptor, allocated + memory area, etc. + + Before any task that uses this utility is loaded (which includes the + erlang emulator), the reclaim.o object file has to be loaded and + the function reclaim_init() has to be called. reclaim_init should be called + only _ONCE_ in a systems lifetime and has only a primitive guard + against multiple calls (i.e. a global variable is checked). Therefore + the initialization should occur either in the start script of the system + or (even better) in the usrInit() part of system initialization. The + object file itself should be loaded only once, so linking it with the + kernel is a good idea, linking with each application is an extremely bad + dito. Make really sure that it's loaded _before_ any application that + uses it if You want to load it in the startup script. + + If You dont want to have #define's for the posix/stdio names + of the file/memory operations (i.e. no #define malloc save_malloc etc), + #define RECLAIM_NO_ALIAS in Your source before reclaim.h is included. +*/ + +#include <vxWorks.h> /* STATUS, size_t */ +#include <sockLib.h> /* struct sockaddr */ +#include <memLib.h> +#include <stdio.h> /* FILE */ + +#if defined(__STDC__) +#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \ +extern RetType FunName ParamList +#define _RECLAIM_VOID_PTR void * +#define _RECLAIM_VOID_PARAM void +#define _RECLAIM_VOID_RETURN void +#elif defined(__cplusplus) +#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \ +extern "C" RetType FunName ParamList +#define _RECLAIM_VOID_PTR void * +#define _RECLAIM_VOID_PARAM +#define _RECLAIM_VOID_RETURN void +#else +#define _RECLAIM_DECL_FUN(RetType, FunName, Ignore) extern RetType FunName() +#define DECLARE_FUNCTION_TYPE(RetType, Type, PList) typedef RetType (* Type)() +#define _RECLAIM_VOID_PTR char * +#define _RECLAIM_VOID_PARAM +#define _RECLAIM_VOID_RETURN +#endif /* __STDC__ / __cplusplus */ + +/* Initialize the facility, on a per system basis. */ +_RECLAIM_DECL_FUN(STATUS, reclaim_init, (_RECLAIM_VOID_PARAM)); + +/* File descriptor operations */ +_RECLAIM_DECL_FUN(int,save_open,(char *, int, ...)); +_RECLAIM_DECL_FUN(int,save_creat,(char *, int)); +_RECLAIM_DECL_FUN(int,save_socket,(int, int, int)); +_RECLAIM_DECL_FUN(int,save_accept,(int, struct sockaddr *, int *)); +_RECLAIM_DECL_FUN(int,save_close,(int)); +/* Interface to add an fd to what's reclaimed even though it's not open with + one of the above functions */ +_RECLAIM_DECL_FUN(_RECLAIM_VOID_RETURN, save_fd, (int fd)); +#ifndef RECLAIM_NO_ALIAS +#define open save_open +#define creat save_creat +#define socket save_socket +#define accept save_accept +#define close save_close +#endif +/* Stdio file operations */ +_RECLAIM_DECL_FUN(FILE *, save_fopen, (const char *, char *)); +_RECLAIM_DECL_FUN(FILE *, save_fdopen, (int, char *)); +_RECLAIM_DECL_FUN(FILE *, save_freopen, (char *, char *, FILE *)); +_RECLAIM_DECL_FUN(int, save_fclose, (FILE *)); +/* XXX Should do opendir/closedir too... */ +#ifndef RECLAIM_NO_ALIAS +#define fopen save_fopen +#define fdopen save_fdopen +#define freopen save_freopen +#define fclose save_fclose +#endif +/* Memory allocation */ +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_malloc, (size_t)); +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_calloc, (size_t, size_t)); +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_realloc, + (_RECLAIM_VOID_PTR, size_t)); +_RECLAIM_DECL_FUN(void, save_free, (_RECLAIM_VOID_PTR)); +_RECLAIM_DECL_FUN(void, save_cfree, (_RECLAIM_VOID_PTR)); +#ifndef RECLAIM_NO_ALIAS +#define malloc save_malloc +#define calloc save_calloc +#define realloc save_realloc +#define free save_free +#define cfree save_cfree +#endif +/* Generic interfaces to malloc etc... */ +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_malloc, (size_t)); +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_realloc, + (_RECLAIM_VOID_PTR, size_t)); +_RECLAIM_DECL_FUN(void, plain_free, (_RECLAIM_VOID_PTR)); +#endif /* _RECLAIM_H */ + + + + diff --git a/erts/etc/vxworks/reclaim_private.h b/erts/etc/vxworks/reclaim_private.h new file mode 100644 index 0000000000..4ed935bee2 --- /dev/null +++ b/erts/etc/vxworks/reclaim_private.h @@ -0,0 +1,44 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#ifndef _RECLAIM_PRIVATE_H +#define _RECLAIM_PRIVATE_H +/* + * Private header for the reclaim facility, also included in the emulator. + */ + +#include "reclaim.h" + +/* Typedefs for ANSI memory allocation function pointers */ +typedef void *(*MallocFunction)(size_t); +typedef void *(*ReallocFunction)(void *, size_t); +typedef void *(*CallocFunction)(size_t, size_t); +typedef void (*FreeFunction)(void *); +typedef STATUS (*CfreeFunction)(char *); + +/* Functions for internal use and use by the emulator */ +extern int reclaim_max_files(void); +extern void set_reclaim_free_function(FreeFunction f); +extern void save_delete_hook(FUNCPTR func, caddr_t parm); +extern void *save_malloc2(size_t size, MallocFunction mf); +extern void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf); +extern void *save_realloc2(void *optr, size_t size, ReallocFunction rf); +extern void save_free2(void *ptr, FreeFunction ff); +extern void save_cfree2(void *ptr, CfreeFunction ff); + +#endif /* _RECLAIM_PRIVATE_H */ diff --git a/erts/etc/vxworks/resolv.conf b/erts/etc/vxworks/resolv.conf new file mode 100644 index 0000000000..85c89d64c4 --- /dev/null +++ b/erts/etc/vxworks/resolv.conf @@ -0,0 +1,6 @@ +domain du.uab.ericsson.se +nameserver 134.138.176.16 +nameserver 136.225.254.224 +nameserver 134.138.128.25 +search du.uab.ericsson.se uab.ericsson.se ericsson.se +lookup bind file diff --git a/erts/etc/vxworks/vxcall.c b/erts/etc/vxworks/vxcall.c new file mode 100644 index 0000000000..3362d05fc5 --- /dev/null +++ b/erts/etc/vxworks/vxcall.c @@ -0,0 +1,145 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <vxWorks.h> +#include <symLib.h> +#include <sysSymTbl.h> +#include <a_out.h> + +extern char *malloc(); +static STATUS lookup(); + +/* + Little utility to convert from Unix' argv,argv calling conventions to + VxWorks' arg0,arg1,arg2,... + Will do limited argument parsing - no parenthesis around nor commas + between the args, which may be "-enclosed strings (without \ escapes), + '-enclosed characters (also no \ escapes), integers, or symbols. +*/ + +int vxcall(argc, argv) +int argc; +char **argv; +{ + int vxarg[10]; /* Max 10 args can be passed */ + FUNCPTR entry; + SYM_TYPE type; + int i, l; + +#ifdef DEBUG + fdprintf(2, "vxcall:"); + for (i = 1; i < argc; i++) + fdprintf(2, " %s", argv[i]); + fdprintf(2, "\n"); +#endif + if (lookup(argv[1], N_EXT | N_TEXT, (char **)&entry) != OK) + return(ERROR); + /* Do limited "C" parsing of the args */ + for (i = 0; i < 10; i++) { + if (i < argc - 2) { + switch (argv[i+2][0]) { + case '"': + l = strlen(argv[i+2]) - 1; + if (argv[i+2][l] != '"') + return(ERROR); + /* just strip the quotes - should do \escapes within... */ + vxarg[i] = (int)&argv[i+2][1]; + argv[i+2][l] = '\0'; + break; + case '\'': + if (argv[i+2][2] != '\'') + return(ERROR); + vxarg[i] = argv[i+2][1]; /* should do \escapes... */ + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + vxarg[i] = atoi(argv[i+2]); /* should do octal, hex, float.. */ + break; + default: + if (lookup(argv[i+2], 0, (char **)&vxarg[i]) != OK) + return(ERROR); + } + } else + vxarg[i] = 0; + } +#ifdef DEBUG + fdprintf(2, "calling 0x%x(0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x)\n", + entry, vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4], + vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]); +#endif + return((*entry)(vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4], + vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9])); +} + +/* Entry point for unix:cmd in post-4.1 erlang - uses "sh -c 'cmd...'" */ +int sh(argc, argv) +int argc; +char **argv; +{ + int ll = strlen(argv[argc-1]) - 1; + +#ifdef DEBUG + int i; + fdprintf(2, "sh:"); + for (i = 1; i < argc; i++) + fdprintf(2, " %s", argv[i]); + fdprintf(2, "\n"); +#endif + if (strcmp(argv[1], "-c") != 0 || + argv[2][0] != '\'' || argv[argc-1][ll] != '\'') + return(ERROR); + argv[argc-1][ll] = '\0'; /* delete trailing ' */ + argv[2]++; /* skip leading ' (*after* the above!) */ + return(vxcall(argc-1, argv+1)); +} + +/* Lookup symbol; get address for text symbols, value (assuming int) + otherwise; return OK or ERROR on failure + Symbol name is null-terminated and without the leading '_' */ +STATUS +lookup(sym, stype, value) +char *sym, **value; +int stype; +{ + char buf[256]; + char *symname = buf; + int len, ret; + SYM_TYPE type; + + len = strlen(sym); + if (len > 254 && (symname = malloc(len+2)) == NULL) + return(ERROR); +#if defined _ARCH_PPC || defined SIMSPARCSOLARIS + /* GCC for PPC or SIMSPARC doesn't add a leading _ to symbols */ + strcpy(symname, sym); +#else + sprintf(symname, "_%s", sym); +#endif + ret = (stype != 0) ? + symFindByNameAndType(sysSymTbl, symname, value, &type, stype, stype) : + symFindByName(sysSymTbl, symname, value, &type); + if (symname != buf) + free(symname); + if (ret == OK && (type & N_TEXT) == 0) /* get value */ + *value = (char *)*((int *)*value); + return(ret); +} diff --git a/erts/etc/vxworks/wd_example.c b/erts/etc/vxworks/wd_example.c new file mode 100644 index 0000000000..0e3a6a1cb2 --- /dev/null +++ b/erts/etc/vxworks/wd_example.c @@ -0,0 +1,141 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * File: frc5te_wd.c + * Purpose: Watchdog NMI handling for FORCE 5TE + * + * Description: + * The watchdog handler routines are system specific. A program that + * wants to utilize a hardware watchdog should call wd_init and test + * the return value. If wd_init returns true (!0); there is a hardware + * watchdog, and that watchdog has been activated. If no watchdog exists, + * wd_init returns false (0). + * + * To keep the watchdog happy, call wd_reset at least every X seconds, + * where X is the number of seconds specified in the call to wd_init. + * + * The watchdog can be disarmed by setting the variable wd_disarmed to 1, + * and armed again by setting the same variable to 0. Watchdog status + * information can be retrieved using the function wd_status. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <frc5e.h> +#include <logLib.h> +#include <taskLib.h> +#include <sysLib.h> +#include <stdio.h> +#include "hw_watchdog.h" + +/* prototypes */ +extern sysNMIConnect(); +#ifdef __STDC__ +void wd_keeper(int); +void wd_nmi_int(UINT8); +void wd_status(void); +#else +void wd_keeper(); +void wd_nmi_int(); +void wd_status(); +#endif + +#define WD_NMI_MIN_DELAY 0.830 /* Min time before watchdog NMI (in seconds) */ +#define WD_RESET_FREQUENCY (WD_NMI_MIN_DELAY / 2) /* how often the timer is reset */ + +#define WD_KEEPER_STACK_SIZE 10000 + +/* global variables */ +extern int spTaskOptions; +static volatile int wd_count_startval; /* start value set by wd_init */ +static volatile int wd_count; /* counter for wd_keeper */ +volatile int wd_disarmed = 0; /* debug feature */ + +/* wd_init is executed to initialize the watchdog. It spawns the task */ +/* wd_keeper and returns true (non-zero) if a hardware watchdog exists, */ +/* or returns false (zero) otherwise. */ +int wd_init(timeout, prio) + int timeout, prio; +{ + taskSpawn("wd_keeper", prio, spTaskOptions, WD_KEEPER_STACK_SIZE, + (FUNCPTR)wd_keeper, timeout,0,0,0,0,0,0,0,0,0); + return !0; /* watchdog exists */ +} + + +/* wd_reset is called as an alive-signal from the supervisor process. */ +/* If there is no call to this function within a certain time, the */ +/* watchdog will reboot the system. */ +void wd_reset() +{ + wd_count = wd_count_startval; +} + + +/* wd_keeper runs as a separate task and resets the watchdog timer */ +/* before an NMI is generated. This task uses the counter wd_count to */ +/* decide if it should exit or keep resetting the timer. */ +/* Note! This task must run with higher priority than the application! */ +void wd_keeper(timeout) + int timeout; +{ + int wd_delay = sysClkRateGet() * WD_RESET_FREQUENCY; + wd_count_startval = (int)(timeout / WD_RESET_FREQUENCY); + wd_count = wd_count_startval; + + /* Connect and enable level 15 interrupts */ + sysNMIConnect((VOIDFUNCPTR) wd_nmi_int, WD_NMI, WD_NMI); + *(char *)FRC5CE_GEN_PURPOSE2_REG |= FRC5CE_NMI_ENABLE; + + while ((wd_count > 0) || wd_disarmed) { + *(char *)FRC5CE_VME_A32MAP_REG |= FRC5CE_WATCHDOG_ENABLE; + taskDelay(wd_delay); + if (!wd_disarmed) wd_count--; + else wd_count = wd_count_startval; + } + logMsg("Watchdog keeper exits. No alive signal from application in %d seconds.\n",wd_count_startval * WD_RESET_FREQUENCY,0,0,0,0,0); +} + + +/* wd_nmi_int is the function connected to the watchdog interrupt. */ +/* It will report the failure to reset the watchdog timer. */ +void wd_nmi_int(type) + UINT8 type; +{ + switch(type) { + case WD_NMI: + logMsg("Watchdog interrupt! System will reboot.\n",0,0,0,0,0,0); + break; + default: + logMsg("Bad type (%d) in call to watchdog interrupt handler.\n",type,0,0,0,0,0); + break; + } +} + + +/* wd_status displays the current value of the counter. */ +void wd_status() +{ + fprintf(stderr, "Watchdog is %sarmed.\n", wd_disarmed ? "dis" : ""); + fprintf(stderr, "Counter value: %d\n", wd_count); + fprintf(stderr, "Start value is: %d (%d seconds)\n", + wd_count_startval, (int)(wd_count_startval * WD_RESET_FREQUENCY)); +} |