Files
epics-base/modules/libcom/RTEMS/posix/rtems_init.c
T
Brendan Chandler 6b866ddc67 Configure Static IP with RTEMS libbsd (#853)
* Add RTEMS libbsd static network config for motload nvram

setBootConfigFromNVRAM() now configures static networking via the
libbsd rtems_bsd_ifconfig() API when building against RTEMS with
libbsd.  It only uses static networking if it can find a configured IP
and netmask from GEV or the mot script boot parameters, otherwise it
falls back to DHCP.

Also note, NTP was part of setBootConfigFromNVRAM() when using legacy
RTEMS networking, but is handled outside this function for LibBSD.  To
preserve the legacy behavior, setBootConfigFromNVRAM() still pulls out
NTP server info, but now stores it in an output parameter for outside
code to use.

* Wait for link to come up when starting RTEMS/libbsd/static IP

When configuring a static IP on RTEMS/libbsd, we need to intentionally
wait for the link to come up before continuing to configure ntp, etc.

* Separate common network setup from BSP specific implementations

All the BSP setBootConfigFromNVRAM() functions ultimately configured
the RTEMS networking the same way.  The common networking code
has been pulled out into a separate function now called from BSP specific
code.

All networking setup is now included and optionally used only
when the BSP sets the field appropriately.

This lets the setBootConfigFromNVRAM functions be independent of the
network stack, just focussing on collecting the configuration from each
BSP as appropriate.

* Fix unused function warnings for functions unused on certain BSPs

* Remove old comments about if MY_BOOTP

* Added release note entry

-- 
Co-authored-by: Brendan Chandler <bchandler@anl.gov>
2026-08-04 17:22:18 -05:00

1322 lines
39 KiB
C

/*************************************************************************\
* Copyright (c) 2002 The University of Saskatchewan
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* RTEMS startup task for EPICS
* Author: W. Eric Norum
* eric.norum@usask.ca
* (306) 966-5394
*/
/*
* This is the first implementation with posix and rtems5.
* Currently only dhcp is supported with libbsd. The reading of the
* environment variables from e.g. u-boot environment is still missing.
* Extensive tests so far only with qemu.
*/
/* trigger sys/syslog.h to emit prioritynames[] */
#define SYSLOG_NAMES
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <rtems.h>
#include <rtems/malloc.h>
#include <rtems/error.h>
#include <rtems/stackchk.h>
#include <rtems/imfs.h>
#include <rtems/shell.h>
#include <pthread.h>
#include <signal.h>
#include <sched.h>
#include <rtems/libio.h>
#include <rtems/rtc.h>
#include <time.h>
#include <sys/unistd.h>
#if __RTEMS_MAJOR__ > 4
#include <rtems/tod.h>
#else
#include <rtems/score/tod.h>
#endif
#ifdef RTEMS_LEGACY_STACK
#include <rtems/rtems_bsdnet.h>
#include <librtemsNfs.h>
#else // libbsd stack
#include <rtems/bsd/bsd.h>
#include <rtems/bsd/modules.h>
#include <rtems/dhcpcd.h>
#include <machine/rtems-bsd-commands.h>
#include <bsp.h>
#include <bsp/nexus-devices.h>
#endif
#include <rtems/telnetd.h>
#if __RTEMS_MAJOR__ > 4
#include <rtems/printer.h>
#endif
#include "epicsVersion.h"
#include "epicsThread.h"
#include "epicsTime.h"
#include "epicsExit.h"
#include "envDefs.h"
#include "errlog.h"
#include "logClient.h"
#include "osiUnistd.h"
#include "iocsh.h"
#include "osdTime.h"
#include "epicsMemFs.h"
#include "epicsEvent.h"
#include "errlog.h"
#include "epicsRtemsInitHooks.h"
#ifndef RTEMS_LEGACY_STACK
// setup own ntp-client to get time
#include "epicsNtp.h"
epicsEventId dhcpDone;
#endif
/* these settings are needed by the rtems startup
* may provide by dhcp/bootp
* or environments from the "BIOS" like u-boot, motboot etc.
*/
char rtemsInit_NTP_server_ip[16] = "";
char bootp_server_name_init[128] = "1001.1001@10.0.5.1:/epics";
char bootp_boot_file_name_init[128] = "/epics/myExample/bin/RTEMS-beatnik/myExample.boot";
char bootp_cmdline_init[128] = "/epics/myExample/iocBoot/iocmyExample/st.cmd";
/* TODO check rtems_bsdnet_bootp_cmdline */
#ifndef RTEMS_LEGACY_STACK
struct in_addr rtems_bsdnet_bootp_server_address;
char *rtems_bsdnet_bootp_server_name = bootp_server_name_init;
char *rtems_bsdnet_bootp_boot_file_name = bootp_boot_file_name_init;
char *rtems_bsdnet_bootp_cmdline = bootp_cmdline_init;
#endif // not LEGACY Stack
/*
* Prototypes for some functions not in header files
*/
//void tzset(void);
int fileno(FILE *);
int main(int argc, char **argv);
static void
logReset (void)
{
void rtems_bsp_reset_cause(char *buf, size_t capacity) __attribute__((weak));
void (*fp)(char *buf, size_t capacity) = rtems_bsp_reset_cause;
if (fp) {
char buf[80];
fp(buf, sizeof buf);
printf ("Startup after %s.\n", buf);
}
else {
printf ("Startup.\n");
}
}
/*
***********************************************************************
* FATAL ERROR REPORTING *
***********************************************************************
*/
/*
* Delay for a while, then terminate
*/
static void
delayedPanic (const char *msg)
{
rtems_task_wake_after (rtems_clock_get_ticks_per_second());
rtems_task_wake_after (rtems_clock_get_ticks_per_second());
rtems_panic ("%s", msg);
}
/*
* Log error and terminate
*/
void
LogFatal (const char *msg, ...)
{
va_list ap;
va_start (ap, msg);
errlogVprintf (msg, ap);
va_end (ap);
delayedPanic (msg);
}
/*
* Log RTEMS error and terminate
*/
void
LogRtemsFatal (const char *msg, rtems_status_code sc)
{
printf ("%s: %s\n", msg, rtems_status_text (sc));
delayedPanic (msg);
}
/*
* Log network error and terminate
*/
void
LogNetFatal (const char *msg, int err)
{
printf ("%s: %d\n", msg, err);
delayedPanic (msg);
}
void *
mustMalloc(int size, const char *msg)
{
void *p;
if ((p = malloc (size)) == NULL)
LogFatal ("Can't allocate space for %s.\n", msg);
return p;
}
/*
***********************************************************************
* REMOTE FILE ACCESS *
***********************************************************************
*/
#ifdef OMIT_NFS_SUPPORT
# include <rtems/tftp.h>
#endif
const epicsMemFS *epicsRtemsFSImage __attribute__((weak));
const epicsMemFS *epicsRtemsFSImage = (void*)&epicsRtemsFSImage;
/* hook to allow app specific FS setup */
int
epicsRtemsMountLocalFilesystem(char **argv) __attribute__((weak));
int
epicsRtemsMountLocalFilesystem(char **argv)
{
if(epicsRtemsFSImage==(void*)&epicsRtemsFSImage)
return -1; /* no FS image provided. */
else if(epicsRtemsFSImage==NULL)
return 0; /* no FS image provided, but none is needed. */
else {
printf("***** Using compiled in file data *****\n");
if (epicsMemFsLoad(epicsRtemsFSImage) != 0) {
printf("Can't unpack tar filesystem\n");
return -1;
} else {
argv[1] = "/";
return 0;
}
}
}
static int
initialize_local_filesystem(char **argv)
{
extern char _DownloadLocation[] __attribute__((weak));
extern char _FlashBase[] __attribute__((weak));
extern char _FlashSize[] __attribute__((weak));
argv[0] = rtems_bsdnet_bootp_boot_file_name;
if (epicsRtemsMountLocalFilesystem(argv)==0) {
return 1; /* FS setup successful */
#ifdef RTEMS_LEGACY_STACK
} else if (_FlashSize && (_DownloadLocation || _FlashBase)) {
extern char _edata[];
size_t flashIndex = _edata - _DownloadLocation;
char *header = _FlashBase + flashIndex;
if (memcmp(header + 257, "ustar ", 8) == 0) {
int fd;
printf ("***** Unpack in-memory file system (IMFS) *****\n");
if (rtems_tarfs_load("/", (unsigned char *)header, (size_t)_FlashSize - flashIndex) != 0) {
printf("Can't unpack tar filesystem\n");
return 0;
}
if ((fd = open(rtems_bsdnet_bootp_cmdline, 0)) >= 0) {
close(fd);
printf ("***** Found startup script (%s) in IMFS *****\n", rtems_bsdnet_bootp_cmdline);
argv[1] = rtems_bsdnet_bootp_cmdline;
return 1;
}
printf ("***** Startup script (%s) not in IMFS *****\n", rtems_bsdnet_bootp_cmdline);
}
#endif /* only with old stack, check check libbsd dependency! */
}
return 0;
}
#ifndef OMIT_NFS_SUPPORT
int
nfsMount(char *uidhost, char *path, char *mntpoint)
{
int devl = strlen(uidhost) + strlen(path) + 2;
char *dev;
int rval = -1;
if ((dev = malloc(devl)) == NULL) {
fprintf(stderr,"nfsMount: out of memory\n");
return -1;
}
sprintf(dev, "%s:%s", uidhost, path);
printf("Mount %s on %s\n", dev, mntpoint);
rval = mount_and_make_target_path (
dev, mntpoint, RTEMS_FILESYSTEM_TYPE_NFS,
RTEMS_FILESYSTEM_READ_WRITE, NULL );
if(rval)
perror("mount failed");
free(dev);
return rval;
}
#define NFS_INIT
#endif
static void
initialize_remote_filesystem(char **argv, int hasLocalFilesystem)
{
#ifdef OMIT_NFS_SUPPORT
printf ("***** Initializing TFTP *****\n");
mount_and_make_target_path(NULL,
"/TFTP",
RTEMS_FILESYSTEM_TYPE_TFTPFS,
RTEMS_FILESYSTEM_READ_WRITE,
NULL);
if (!hasLocalFilesystem) {
char *path;
int pathsize = 200;
int l;
path = mustMalloc(pathsize, "Command path name ");
strcpy (path, "/TFTP/BOOTP_HOST/epics/");
l = strlen (path);
if (gethostname (&path[l], pathsize - l - 10) || (path[l] == '\0'))
{
LogFatal ("Can't get host name");
}
strcat (path, "/st.cmd");
argv[1] = path;
}
#else
char *server_name;
char *server_path;
char *mount_point;
char *cp;
int l = 0;
printf ("***** Initializing NFS *****\n");
NFS_INIT
if (env_nfsServer && env_nfsPath && env_nfsMountPoint) {
server_name = env_nfsServer;
server_path = env_nfsPath;
mount_point = env_nfsMountPoint;
cp = mount_point;
while ((cp = strchr(cp+1, '/')) != NULL) {
*cp = '\0';
if ((mkdir (mount_point, 0755) != 0)
&& (errno != EEXIST))
LogFatal("Can't create directory \"%s\": %s.\n",
mount_point, strerror(errno));
*cp = '/';
}
argv[1] = rtems_bsdnet_bootp_cmdline;
}
else if (hasLocalFilesystem) {
return;
}
else {
/*
* Use first component of nvram/bootp command line pathname
* to set up initial NFS mount. A "/tftpboot/" is prepended
* if the pathname does not begin with a '/'. This allows
* NFS and TFTP to have a similar view of the remote system.
*/
if (rtems_bsdnet_bootp_cmdline[0] == '/')
cp = rtems_bsdnet_bootp_cmdline + 1;
else
cp = rtems_bsdnet_bootp_cmdline;
cp = strchr(cp, '/');
if ((cp == NULL)
|| ((l = cp - rtems_bsdnet_bootp_cmdline) == 0))
LogFatal("\"%s\" is not a valid command pathname.\n", rtems_bsdnet_bootp_cmdline);
cp = mustMalloc(l + 20, "NFS mount paths");
server_path = cp;
server_name = rtems_bsdnet_bootp_server_name;
if (rtems_bsdnet_bootp_cmdline[0] == '/') {
mount_point = server_path;
strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l);
mount_point[l] = '\0';
argv[1] = rtems_bsdnet_bootp_cmdline;
/*
* Its probably common to embed the mount point in the server
* name so, when this is occurring, don't clobber the mount point
* by appending the first node from the command path. This allows
* the mount point to be a different path then the server's mount
* path.
*
* This allows for example a line similar to as follows the DHCP
* configuration file.
*
* server-name "159.233@192.168.0.123:/vol/vol0/bootRTEMS";
*/
if ( server_name ) {
const size_t allocSize = strlen ( server_name ) + 2;
char * const pServerName = mustMalloc( allocSize,
"NFS mount paths");
char * const pServerPath = mustMalloc ( allocSize,
"NFS mount paths");
const int scanfStatus = sscanf (
server_name,
"%[^:] : / %s",
pServerName,
pServerPath + 1u );
if ( scanfStatus == 2 ) {
pServerPath[0u]= '/';
server_name = pServerName;
// is this a general solution?
server_path = mount_point = pServerPath;
}
else {
free ( pServerName );
free ( pServerPath );
}
}
}
else {
char *abspath = mustMalloc(strlen(rtems_bsdnet_bootp_cmdline)+2,"Absolute command path");
strcpy(server_path, "/tftpboot/");
mount_point = server_path + strlen(server_path);
strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l);
mount_point[l] = '\0';
mount_point--;
strcpy(abspath, "/");
strcat(abspath, rtems_bsdnet_bootp_cmdline);
argv[1] = abspath;
}
}
printf("nfsMount(\"%s\", \"%s\", \"%s\")\n",
server_name, server_path, mount_point);
nfsMount(server_name, server_path, mount_point);
#endif
}
static
char rtems_etc_hosts[] = "127.0.0.1 localhost\n";
/* If it doesn't already exist, create /etc/hosts with an entry for 'localhost' */
static
void fixup_hosts(void)
{
FILE *fp;
int ret;
struct stat STAT;
ret=stat("/etc/hosts", &STAT);
if(ret==0)
{
return; /* already exists, assume file */
} else if(errno!=ENOENT) {
perror("error: fixup_hosts stat /etc/hosts");
return;
}
ret = mkdir("/etc", 0775);
if(ret!=0 && errno!=EEXIST)
{
perror("error: fixup_hosts create /etc");
return;
}
if((fp=fopen("/etc/hosts", "w"))==NULL)
{
perror("error: fixup_hosts create /etc/hosts");
}
if(fwrite(rtems_etc_hosts, 1, sizeof(rtems_etc_hosts)-1, fp)!=sizeof(rtems_etc_hosts)-1)
{
perror("error: failed to write /etc/hosts");
}
fclose(fp);
}
/*
* Get to the startup script directory
* The TFTP filesystem requires a trailing '/' on chdir arguments.
*/
static void
set_directory (const char *commandline)
{
const char *cp;
char *directoryPath;
int l;
cp = strrchr(commandline, '/');
if (cp == NULL) {
l = 0;
cp = "/";
}
else {
l = cp - commandline;
cp = commandline;
}
directoryPath = mustMalloc(l + 2, "Command path directory ");
strncpy(directoryPath, cp, l);
directoryPath[l] = '/';
directoryPath[l+1] = '\0';
if (chdir (directoryPath) < 0)
LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno));
else
printf("chdir(\"%s\")\n", directoryPath);
free(directoryPath);
}
/*
***********************************************************************
* RTEMS/EPICS COMMANDS *
***********************************************************************
*/
static const iocshArg rtshellArg0 = { "cmd", iocshArgString};
static const iocshArg rtshellArg1 = { "args", iocshArgArgv};
static const iocshArg * rtshellArgs[2] = { &rtshellArg0, &rtshellArg1};
static const iocshFuncDef rtshellFuncDef = { "rt",2, rtshellArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
, "run rtems shell command"
#endif
};
static void rtshellCallFunc(const iocshArgBuf *args)
{
rtems_shell_cmd_t *cmd = rtems_shell_lookup_cmd(args[0].sval);
int ret;
if (!cmd) {
fprintf(stderr, "ERR: No such command\n");
iocshSetError(-1);
} else {
fflush(stdout);
fflush(stderr);
ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av);
fflush(stdout);
fflush(stderr);
iocshSetError(ret);
if(ret)
fprintf(stderr, "ERR: %d\n",ret);
}
}
/*
* RTEMS status
*/
static void
rtems_netstat (unsigned int level)
{
#ifndef RTEMS_LEGACY_STACK
printf ("***** Sorry not implemented yet with the new network stack (bsdlib)\n");
#else
rtems_bsdnet_show_if_stats ();
rtems_bsdnet_show_mbuf_stats ();
if (level >= 1) {
rtems_bsdnet_show_inet_routes ();
}
if (level >= 2) {
rtems_bsdnet_show_ip_stats ();
rtems_bsdnet_show_icmp_stats ();
rtems_bsdnet_show_udp_stats ();
rtems_bsdnet_show_tcp_stats ();
}
#endif
}
static const iocshArg netStatArg0 = { "level",iocshArgInt};
static const iocshArg * const netStatArgs[1] = {&netStatArg0};
static const iocshFuncDef netStatFuncDef = {"netstat",1,netStatArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
, "show network status"
#endif
};
static void netStatCallFunc(const iocshArgBuf *args)
{
rtems_netstat(args[0].ival);
}
static const iocshFuncDef heapSpaceFuncDef = {"heapSpace",0,NULL
#ifdef IOCSHFUNCDEF_HAS_USAGE
, "show malloc statistic"
#endif
};
static void heapSpaceCallFunc(const iocshArgBuf *args)
{
#if __RTEMS_MAJOR__ > 4
Heap_Information_block info;
double x;
malloc_info (&info);
x = info.Stats.size - (unsigned long)
(info.Stats.lifetime_allocated - info.Stats.lifetime_freed);
if (x >= 1024*1024)
printf("Heap space: %.1f MB\n", x / (1024 * 1024));
else
printf("Heap space: %.1f kB\n", x / 1024);
#else
rtems_malloc_statistics_t s;
double x;
malloc_get_statistics(&s);
x = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed);
if (x >= 1024*1024)
printf("Heap space: %.1f MB\n", x / (1024 * 1024));
else
printf("Heap space: %.1f kB\n", x / 1024);
#endif // RTEMS < 5
}
#ifndef OMIT_NFS_SUPPORT
static const iocshArg nfsMountArg0 = { "[uid.gid@]host",iocshArgString};
static const iocshArg nfsMountArg1 = { "server path",iocshArgString};
static const iocshArg nfsMountArg2 = { "mount point",iocshArgString};
static const iocshArg * const nfsMountArgs[3] = {&nfsMountArg0,&nfsMountArg1,
&nfsMountArg2};
static const iocshFuncDef nfsMountFuncDef = {"nfsMount",3,nfsMountArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
, "mount nfs drive"
#endif
};
static void nfsMountCallFunc(const iocshArgBuf *args)
{
char *cp = args[2].sval;
while ((cp = strchr(cp+1, '/')) != NULL) {
*cp = '\0';
if ((mkdir (args[2].sval, 0755) != 0) && (errno != EEXIST)) {
printf("Can't create directory \"%s\": %s.\n",
args[2].sval, strerror(errno));
return;
}
*cp = '/';
}
iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
}
#endif
int zoneset(const char *zone)
{
int ret;
if(zone) {
if ((ret = setenv("TZ", zone, 1)) < 0)
return ret;
}
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
else if ((ret = unsetenv("TZ")) < 0)
return ret;
#else
else
unsetenv("TZ");
#endif
tzset();
return 0;
}
static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
static const iocshArg * const zonesetArgs[1] = {&zonesetArg0};
static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
, "set timezone (obsolete?)"
#endif
};
static void zonesetCallFunc(const iocshArgBuf *args)
{
iocshSetError(zoneset(args[0].sval));
}
#ifndef RTEMS_LEGACY_STACK
static int bsd_log_to_erl(int level, const char *fmt, va_list ap)
{
return errlogVprintf(fmt, ap);
}
#endif // RTEMS_LEGACY_STACK
static void setlogmaskCallFunc(const iocshArgBuf *args)
{
const char* name = args[0].sval;
const CODE* cur;
if(!name) {
printf("Usage: setlogmask <level>\n"
"\n"
" Level names:\n");
for(cur = prioritynames; cur->c_name; cur++) {
printf(" %s\n", cur->c_name);
}
} else {
for(cur = prioritynames; cur->c_name; cur++) {
if(strcmp(name, cur->c_name)!=0)
continue;
(void)setlogmask(LOG_MASK(cur->c_val));
#ifndef RTEMS_LEGACY_STACK
rtems_bsd_setlogpriority(name);
#endif
return;
}
printf("Error: unknown log level.\n");
iocshSetError(-1);
}
}
static const iocshArg setlogmaskArg0 = {"level name", iocshArgString};
static const iocshArg * const setlogmaskArgs[1] = {&setlogmaskArg0};
static const iocshFuncDef setlogmaskFuncDef = {"setlogmask",1,setlogmaskArgs,
"Set syslog() threshold level"};
/*
* Register RTEMS-specific commands
*/
static void iocshRegisterRTEMS (void)
{
iocshRegister(&netStatFuncDef, netStatCallFunc);
iocshRegister(&heapSpaceFuncDef, heapSpaceCallFunc);
#ifndef OMIT_NFS_SUPPORT
iocshRegister(&nfsMountFuncDef, nfsMountCallFunc);
#endif
iocshRegister(&zonesetFuncDef, &zonesetCallFunc);
iocshRegister(&rtshellFuncDef, &rtshellCallFunc);
iocshRegister(&setlogmaskFuncDef, &setlogmaskCallFunc);
#if __RTEMS_MAJOR__ > 4
rtems_shell_init_environment();
#endif
}
/*
* Set up the console serial line (no handshaking)
*/
static void
initConsole (void)
{
struct termios t;
printf("\n initConsole --- Info ---\n");
printf("stdin: fileno: %d, ttyname: %s\n", fileno(stdin), ttyname(fileno(stdin)));
printf("stdout: fileno: %d, ttyname: %s\n", fileno(stdout), ttyname(fileno(stdout)));
printf("stderr: fileno: %d, ttyname: %s\n", fileno(stderr), ttyname(fileno(stderr)));
if (tcgetattr (fileno (stdin), &t) < 0) {
printf ("tcgetattr failed: %s\n", strerror (errno));
return;
}
t.c_iflag &= ~(IXOFF | IXON | IXANY);
if (tcsetattr (fileno (stdin), TCSANOW, &t) < 0) {
printf ("tcsetattr failed: %s\n", strerror (errno));
return;
}
}
/*
* Hook to ensure that BSP cleanup code gets run on exit
*/
static void
exitHandler(void)
{
rtems_shutdown_executive(0);
}
#ifndef RTEMS_LEGACY_STACK
static void
dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env)
{
int bound = 0;
char interface[16] = "?";
char reason[16] = "?";
char new_host_name[32] = "RTEMShost";
struct dhcp_vars_t {
const char * const name;
char* var;
size_t varsize;
} dhcp_vars[] = {
#define DEFVAR(N) { #N, N, sizeof(N) }
DEFVAR(interface),
DEFVAR(reason),
DEFVAR(new_host_name),
#undef DEFVAR
#define DEFVAR(N, V) { N, V, sizeof(V) }
DEFVAR("new_ntp_servers", rtemsInit_NTP_server_ip),
DEFVAR("new_tftp_server_name", bootp_server_name_init),
DEFVAR("new_bootfile_name", bootp_boot_file_name_init),
DEFVAR("new_rtems_cmdline", bootp_cmdline_init),
#undef DEFVAR
{NULL}
};
(void)hook;
printf("\n");
for (;NULL != *env;++env) {
printf("dhcpcd ---> '%s'\n", *env);
for(const struct dhcp_vars_t* var = dhcp_vars; var->name; var++) {
size_t namelen = strlen(var->name);
// assume "KEY=value" with no whitespace around '='
if(strncmp(*env, var->name, namelen)!=0 || (*env)[namelen]!='=')
continue;
const char* value = *env + namelen+1;
size_t valuelen = strlen(value);
// require ordering of keys:
// interface=
// reason=
// all others...
if(var->var!=reason && var->var!=interface && !bound) {
// ignore others if !BOUND
} else if(valuelen >= var->varsize) {
printf(" Not enough space for value string. Expand buffer and recompile.\n");
} else {
memcpy(var->var, value, valuelen);
var->var[valuelen] = '\0';
printf(" '%s' = '%s'\n", var->name, var->var);
if(var->var==reason && strcmp(reason, "BOUND")==0) {
printf(" is BOUND\n");
bound = 1;
}
}
break;
}
}
if(bound) {
sethostname (new_host_name, strlen (new_host_name));
epicsEventSignal(dhcpDone);
printf("dhcpd BOUND\n");
}
}
static rtems_dhcpcd_hook dhcpcd_hook = {
.name = "ioc boot",
.handler = dhcpcd_hook_handler
};
static void
default_network_on_exit(int exit_code, void *arg)
{
rtems_printer printer;
(void)arg;
rtems_print_printer_printf(&printer);
rtems_stack_checker_report_usage_with_plugin(&printer);
}
static void
default_network_set_self_prio(rtems_task_priority prio)
{
rtems_status_code sc;
sc = rtems_task_set_priority(RTEMS_SELF, prio, &prio);
assert(sc == RTEMS_SUCCESSFUL);
}
static void
default_network_dhcpcd(void)
{
static const char default_cfg[] = "clientid test client\n";
rtems_status_code sc;
int fd;
int rv;
ssize_t n;
struct stat statbuf;
if (ENOENT == stat("/etc/dhcpcd.conf", &statbuf)) {
fd = open("/etc/dhcpcd.conf", O_CREAT | O_WRONLY,
S_IRWXU | S_IRWXG | S_IRWXO);
assert(fd >= 0);
n = write(fd, default_cfg, sizeof(default_cfg) - 1);
assert(n == (ssize_t) sizeof(default_cfg) - 1);
static const char fhi_cfg[] =
"nodhcp6\n"
"ipv4only\n"
"option ntp-servers\n"
"option rtems_cmdline\n"
"option tftp-server-name\n"
"option bootfile-name\n"
"define 129 string rtems_cmdline\n"
"timeout 0";
n = write(fd, fhi_cfg, sizeof(fhi_cfg) - 1);
assert(n == (ssize_t) sizeof(fhi_cfg) - 1);
rv = close(fd);
assert(rv == 0);
}
sc = rtems_dhcpcd_start(NULL);
assert(sc == RTEMS_SUCCESSFUL);
}
/*
* Block until the named interface reports link up via an RTM_IFINFO routing
* message, or until timeout_secs elapses.
*
* route_sock must already be open (opened before the interface was configured
* so that no RTM_IFINFO event can be missed).
*
* This function was modeled after the RTM_IFINFO handling in
* rtems-libbsd/dhcpcd/if-bsd.c (manage_link). There's a
* simpler implementation in a test that uses a sleep loop in
* rtems-libbsd/testsuite/include/rtems/bsd/test/default-init.h, but
* we chose this more responsive event based implementation.
*
* Returns 0 if link came up, -1 on timeout or error.
*/
static int
wait_for_link_up(int route_sock, const char *ifname, int timeout_secs)
{
printf("Waiting for link on %s (timeout %ds)...\n", ifname, timeout_secs);
struct timeval tv = { .tv_sec = timeout_secs, .tv_usec = 0 };
setsockopt(route_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
char buf[sizeof(struct if_msghdr) + sizeof(struct sockaddr_dl)];
ssize_t n;
while ((n = recv(route_sock, buf, sizeof(buf), 0)) > 0) {
struct rt_msghdr *rtm = (struct rt_msghdr *)(void *)buf;
if (rtm->rtm_type == RTM_IFINFO) {
struct if_msghdr *ifm = (struct if_msghdr *)(void *)buf;
char name[IFNAMSIZ];
if (if_indextoname(ifm->ifm_index, name) != NULL &&
strcmp(name, ifname) == 0 &&
ifm->ifm_data.ifi_link_state == LINK_STATE_UP) {
return 0;
}
}
}
/* recv returned <= 0: SO_RCVTIMEO expired (EAGAIN) or socket error */
printf("Warning: link did not come up on %s within %ds\n",
ifname, timeout_secs);
return -1;
}
#endif // not RTEMS_LEGACY_STACK
#if __RTEMS_MAJOR__>4
/*
***********************************************************************
* TELNET DAEMON *
***********************************************************************
*/
#define LINE_SIZE 256
static void
telnet_pseudoIocsh(char *name, __attribute__((unused))void *arg)
{
char line[LINE_SIZE];
int fid[3], save_fid[3];
printf("info: pty dev name = %s\n", name);
save_fid[1] = dup2(1,1);
fid[1] = dup2( fileno(stdout), 1);
if (fid[1] == -1 ) printf("Can't dup stdout\n");
save_fid[2] = dup2(2,2);
fid[2] = dup2( fileno(stderr), 2);
if (fid[2] == -1 ) printf("Can't dup stderr\n");
const char *prompt = "tIocSh> ";
while (1) {
fputs(prompt, stdout);
fflush(stdout);
/* telnet close not detected ??? tbd */
if (fgets(line, LINE_SIZE, stdin) == NULL) {
dup2(save_fid[1],1);
dup2(save_fid[2],2);
return;
}
if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
if (!strncmp( line, "bye",3)) {
printf( "%s", "Will end session\n");
dup2(save_fid[1],1);
dup2(save_fid[2],2);
return;
}
iocshCmd(line);
}
}
#define SHELL_ENTRY telnet_pseudoIocsh
/*
* Telnet daemon configuration
* 0 or NULL for most fields in this struct indicate default values to RTEMS.
*/
rtems_telnetd_config_table rtems_telnetd_config = {
.command = SHELL_ENTRY,
.arg = NULL,
.priority = 0,
.stack_size = 0,
.client_maximum = 0,
.login_check = NULL,
.keep_stdio = false
};
#endif
/*
* RTEMS Startup task
*/
void *
POSIX_Init ( void *argument __attribute__((unused)))
{
int result;
char *argv[3] = { NULL, NULL, NULL };
rtems_status_code sc;
struct timespec now;
char timeBuff[100];
initConsole ();
/*
* Use BSP-supplied time of day if available otherwise supply default time.
* It is very likely that other time synchronization facilities in EPICS
* will soon override this value.
*/
/* check for RTC ... unfortunately seems to be missing with libbsd and qemu ?
if (checkRealtime() >= 0) {
setRealTimeToRTEMS();
} else
*/
{
// set time to 14.4.2014
now.tv_sec = 1397460606;
now.tv_nsec = 0;
sc = clock_settime(CLOCK_REALTIME, &now);
if (sc < 0)
printf ("***** Can't set time: %s\n", rtems_status_text (sc));
}
sc = clock_gettime( CLOCK_REALTIME, &now);
if ( sc < 0) {
printf ("***** Can't get time: %s\n", rtems_status_text (sc));
} else {
strftime(timeBuff, sizeof timeBuff, "%D %T", gmtime(&now.tv_sec));
printf("time set to : %s.%09ld UTC\n", timeBuff, now.tv_nsec);
}
/*
* Explain why we're here
*/
logReset();
/* TBD ...
* Architecture-specific hooks
*/
#ifdef RTEMS_LEGACY_STACK
if (epicsRtemsInitPreSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0)
delayedPanic("epicsRtemsInitPreSetBootConfigFromNVRAM");
if (rtems_bsdnet_config.bootp == NULL) {
extern int setBootConfigFromNVRAM(char *, size_t);
setBootConfigFromNVRAM(NULL, 0);
}
if (epicsRtemsInitPostSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0)
delayedPanic("epicsRtemsInitPostSetBootConfigFromNVRAM");
#endif
/*
* Override RTEMS Posix configuration, it gets started with posix prio 2
*/
epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityIocsh);
/*
* Create a reasonable environment
*/
putenv ("TERM=xterm");
putenv ("IOCSH_HISTSIZE=20");
/*
* Display some OS information
*/
printf("\n***** RTEMS Version: %s *****\n",
rtems_get_version_string());
#ifndef RTEMS_LEGACY_STACK
#if defined(QEMU_FIXUPS) && defined(__i386__)
// glorious hack to stub out useless EEPROM check
// which takes sooooo longggg w/ QEMU
// Writes a 'ret' instruction to immediately return to the caller
extern void _bsd_e1000_validate_nvm_checksum(void);
*(char*)&_bsd_e1000_validate_nvm_checksum = 0xc3;
#endif
/*
* Start network (libbsd)
*
* start qemu like this
* qemu-system-i386 -m 64 -no-reboot -serial stdio -display none \
* -net nic,model=e1000 -net user,restrict=yes \
* -append "--video=off --console=/dev/com1" -kernel libComTestHarness
*/
printf("\n***** Initializing network (libbsd, dhcpcd) *****\n");
rtems_bsd_setlogpriority("debug");
on_exit(default_network_on_exit, NULL);
/* Let other tasks run to complete background work */
default_network_set_self_prio(RTEMS_MAXIMUM_PRIORITY - 1U);
sc = rtems_bsd_initialize();
assert(sc == RTEMS_SUCCESSFUL);
/* Let the callout timer allocate its resources */
sc = rtems_task_wake_after(2);
assert(sc == RTEMS_SUCCESSFUL);
printf("\n***** ifconfig lo0 *****\n");
rtems_bsd_ifconfig_lo0();
/* Check whether global environment static network config exists.
* If so, use that, else, fall back to DHCP. */
extern int setBootConfigFromNVRAM(char *, size_t);
/* Open route socket before configuring the interface so that no
* events are missed. */
int route_sock = socket(PF_ROUTE, SOCK_RAW, 0);
if (route_sock < 0)
printf("Warning: could not open PF_ROUTE socket: %s\n", strerror(errno));
printf("\n***** checking for static network configuration *****\n");
int status = setBootConfigFromNVRAM(rtemsInit_NTP_server_ip,
sizeof(rtemsInit_NTP_server_ip));
bool try_dhcp = status != 0;
if (try_dhcp) {
/* Route socket not needed — DHCP path has its own event mechanism */
if (route_sock >= 0) {
close(route_sock);
route_sock = -1;
}
printf("\n***** add dhcpcd hook *****\n");
dhcpDone = epicsEventMustCreate(epicsEventEmpty);
rtems_dhcpcd_add_hook(&dhcpcd_hook);
printf("\n***** Start default network dhcpcd *****\n");
default_network_dhcpcd();
/* this seems to be hard coded in the BSP -> Sebastian Huber ? */
printf("\n--Info (hpj)-- bsd task prio IRQS: %d -----\n", rtems_bsd_get_task_priority("IRQS"));
printf("\n--Info (hpj)-- bsd task prio TIME: %d -----\n", rtems_bsd_get_task_priority("TIME"));
// wait for dhcp done ... should be if SYNCDHCP is used
epicsEventWaitStatus stat;
printf("\n ---- Waiting for DHCP ...\n");
stat = epicsEventWaitWithTimeout(dhcpDone, 600);
if (stat == epicsEventOK)
epicsEventDestroy(dhcpDone);
else if (stat == epicsEventWaitTimeout)
printf("\n ---- DHCP timed out!\n");
else
printf("\n ---- dhcpDone Event Unknown state %d\n", stat);
} else {
/* Static IP: block until the physical link comes up before
* proceeding to NTP and NFS, analogous to the DHCP event wait. */
if (route_sock >= 0) {
char ifnamebuf[IF_NAMESIZE];
char *ifname = if_indextoname(1, ifnamebuf);
if (ifname != NULL)
wait_for_link_up(route_sock, ifname, 30);
close(route_sock);
}
}
{ // Dump ifconfig and netstat info to stdout
const char* ifconfg_args[] = {
"ifconfig", NULL
};
const char* netstat_args[] = {
"netstat", "-rn", NULL
};
printf("-------------- IFCONFIG -----------------\n");
rtems_bsd_command_ifconfig(1, (char**) ifconfg_args);
printf("-------------- NETSTAT ------------------\n");
rtems_bsd_command_netstat(2, (char**) netstat_args);
}
/* until now there is no NTP support in libbsd -> Sebastian Huber ... */
printf("\n***** Until now no NTP support in RTEMS 5 with rtems-libbsd *****\n");
printf("\n***** Ask ntp server once... *****\n");
if (rtemsInit_NTP_server_ip[0]=='\0') {
printf ("***** No NTP server ...\n");
} else if (epicsNtpGetTime(rtemsInit_NTP_server_ip, &now) < 0) {
printf ("***** Can't get time from ntp ...\n");
} else {
if (clock_settime(CLOCK_REALTIME, &now) < 0){
printf ("***** Can't set time: %s\n", rtems_status_text (sc));
} else {
strftime(timeBuff, sizeof timeBuff, "%D %T", gmtime(&now.tv_sec));
printf("time from ntp : %s.%09ld UTC\n", timeBuff, now.tv_nsec);
}
}
#else // Legacy stack, old network initialization
char *cp;
if ((cp = getenv("EPICS_TS_NTP_INET")) != NULL)
rtems_bsdnet_config.ntp_server[0] = cp;
int rtems_bsdnet_ntpserver_count = 1;
struct in_addr rtems_bsdnet_ntpserver[rtems_bsdnet_ntpserver_count];
memcpy(rtems_bsdnet_ntpserver, rtems_bsdnet_config.ntp_server, sizeof(struct in_addr));
if (rtems_bsdnet_config.network_task_priority == 0)
{
unsigned int p;
if (epicsThreadHighestPriorityLevelBelow(epicsThreadPriorityScanLow, &p)
== epicsThreadBooleanStatusSuccess)
{
rtems_bsdnet_config.network_task_priority = p;
}
}
printf("\n***** Initializing network (Legacy Stack) *****\n");
rtems_bsdnet_initialize_network();
printf("\n***** Network Status *****\n");
rtems_netstat(3);
rtems_bsdnet_synchronize_ntp (0, 0);
#endif // not RTEMS_LEGACY_STACK
printf("\n***** Setting up file system *****\n");
initialize_remote_filesystem(argv, initialize_local_filesystem(argv));
fixup_hosts();
/*
* More environment: iocsh prompt and hostname
*/
{
char hostname[1024];
gethostname(hostname, 1023);
char *cp = mustMalloc(strlen(hostname)+3, "iocsh prompt");
sprintf(cp, "%s> ", hostname);
epicsEnvSet ("IOCSH_PS1", cp);
epicsEnvSet("IOC_NAME", hostname);
}
if (getenv("") == NULL) {
const char *tzp = envGetConfigParamPtr(&EPICS_TZ);
if (tzp == NULL) {
printf("Warning -- no timezone information available -- times will be displayed as GMT.\n");
}
}
tzset();
printf(" check for time registered , C++ initialization ...\n");
//osdTimeRegister();
/*/Volumes/Epics/myExample/bin/RTEMS-xilinx_zynq_a9_qemu
* Run the EPICS startup script
*/
#if __RTEMS_MAJOR__>4
// if telnetd is requested ...
// printf(" Will try to start telnetd with prio %d ...\n", rtems_telnetd_config.priority);
// result = rtems_telnetd_initialize();
// printf (" telnetd initialized with result %d\n", result);
#endif
#if 0
// Start an rtems shell before main, for debugging RTEMS system issues
rtems_shell_init("SHLL", RTEMS_MINIMUM_STACK_SIZE * 4,
100, "/dev/console",
false, true,
NULL);
#endif
printf ("***** Preparing EPICS application *****\n");
iocshRegisterRTEMS ();
set_directory (argv[1]);
epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]);
atexit(exitHandler);
printf ("***** Starting EPICS application *****\n");
#ifndef RTEMS_LEGACY_STACK
// switch OS to async logging
rtems_bsd_set_vprintf_handler(bsd_log_to_erl);
#endif
#if 0
// Start an rtems shell before main, for debugging RTEMS system issues
rtems_shell_init("SHLL", RTEMS_MINIMUM_STACK_SIZE * 4,
100, "/dev/console",
false, true,
NULL);
#endif
result = main ((sizeof argv / sizeof argv[0]) - 1, argv);
printf ("***** IOC application terminating *****\n");
epicsThreadSleep(1.0);
epicsExit(result);
#if defined(__rtems__)
delayedPanic("will reset rtems ... end of POSIX_Init");
#endif
exit(0);
}
#if defined(QEMU_FIXUPS)
/* Override some hooks (weak symbols)
* if BSP defaults aren't configured for running tests.
*/
/* Ensure that stdio goes to serial (so it can be captured) */
#if defined(__i386__) && !USE_COM1_AS_CONSOLE
#include <uart.h>
#if __RTEMS_MAJOR__ > 4
#include <libchip/serial.h>
#endif
extern int BSPPrintkPort;
void bsp_predriver_hook(void)
{
#if __RTEMS_MAJOR__ > 4
Console_Port_Minor = BSP_CONSOLE_PORT_COM1;
#else
BSPConsolePort = BSP_CONSOLE_PORT_COM1;
#endif
BSPPrintkPort = BSP_CONSOLE_PORT_COM1;
}
#endif
/* reboot immediately when done. */
#if defined(__i386__) && BSP_PRESS_KEY_FOR_RESET
void bsp_cleanup(void)
{
void bsp_reset();
bsp_reset();
}
#endif
#endif /* QEMU_FIXUPS */
int cexpdebug __attribute__((weak));