2021-10-11 18:37:13 -03:00

602 lines
11 KiB
C

#ident "@(#)support.c 1.1 10/31/94 SMI"
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <sys/param.h>
#include <sys/user.h>
#include <time.h>
#include <tzfile.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <machine/mmu.h>
#include <machine/pte.h>
#include <machine/reg.h>
#include <debug/debugger.h>
#include <sys/ptrace.h>
#include <varargs.h>
#ifdef sparc
#include <machine/debug/allregs.h>
u_int npmgrps;
u_int segmask;
#endif
#ifdef OPENPROMS
extern void prom_exit_to_mon(), prom_enter_mon(), prom_putchar();
#else OPENPROMS
#define prom_exit_to_mon() (*romp->v_exit_to_mon)()
#define prom_enter_mon() (*romp->v_abortent)()
#define prom_putchar(c) (*romp->v_putchar)((c))
#define prom_mayget() (*romp->v_mayget)()
#endif OPENPROMS
_exit()
{
prom_exit_to_mon();
}
kadb_done()
{
prom_enter_mon();
}
int interrupted = 0;
getchar()
{
register int c;
while ((c = prom_mayget()) == -1)
;
if (c == '\r')
c = '\n';
if (c == 0177 || c == '\b') {
putchar('\b');
putchar(' ');
c = '\b';
}
putchar(c);
return (c);
}
/*
* Read a line into the given buffer and handles
* erase (^H or DEL), kill (^U), and interrupt (^C) characters.
* This routine ASSUMES a maximum input line size of LINEBUFSZ
* to guard against overflow of the buffer from obnoxious users.
*/
gets(buf)
char buf[];
{
register char *lp = buf;
register c;
for (;;) {
c = getchar() & 0177;
switch(c) {
case '\n':
case '\r':
*lp++ = '\0';
return;
case '\b':
lp--;
if (lp < buf)
lp = buf;
continue;
case 'u'&037: /* ^U */
lp = buf;
putchar('^');
putchar('U');
putchar('\n');
continue;
case 'c'&037:
dointr(1);
/*MAYBE REACHED*/
/* fall through */
default:
if (lp < &buf[LINEBUFSZ-1]) {
*lp++ = c;
} else {
putchar('\b');
putchar(' ');
putchar('\b');
}
break;
}
}
}
dointr(doit)
{
putchar('^');
putchar('C');
interrupted = 1;
if (abort_jmp && doit) {
_longjmp(abort_jmp, 1);
/*NOTREACHED*/
}
}
/*
* Check for ^C on input
*/
tryabort(doit)
{
if (prom_mayget() == ('c' & 037)) {
dointr(doit);
/*MAYBE REACHED*/
}
}
/*
* Implement pseudo ^S/^Q processing along w/ handling ^C
* We need to strip off high order bits as monitor cannot
* reliably figure out if the control key is depressed when
* prom_mayget is called in certain circumstances.
* Unfortunately, this means that s/q will work as well
* as ^S/^Q and c as well as ^C when this guy is called.
*/
trypause()
{
register int c;
c = prom_mayget() & 037;
if (c == ('s' & 037)) {
while ((c = prom_mayget() & 037) != ('q' & 037)) {
if (c == ('c' & 037)) {
dointr(1);
/*MAYBE REACHED*/
}
}
} else if (c == ('c' & 037)) {
dointr(1);
/*MAYBE REACHED*/
}
}
/*
* Scaled down version of C Library printf.
*/
/*VARARGS1*/
printf(fmt, va_alist)
char *fmt;
va_dcl
{
va_list x1;
tryabort(1);
va_start(x1);
prf(fmt, x1);
va_end(x1);
}
prf(fmt, adx)
register char *fmt;
register va_list adx;
{
register int b, c;
register char *s;
loop:
while ((c = *fmt++) != '%') {
if(c == '\0')
return;
putchar(c);
}
again:
c = *fmt++;
switch (c) {
case 'l':
goto again;
case 'x': case 'X':
b = 16;
goto number;
case 'd': case 'D':
case 'u': /* what a joke */
b = 10;
goto number;
case 'o': case 'O':
b = 8;
number:
printn(va_arg(adx, u_long), b);
break;
case 'c':
b = va_arg(adx, int);
putchar(b);
break;
case 's':
s = va_arg(adx, char *);
while (c = *s++)
putchar(c);
break;
}
goto loop;
}
/*
* Printn prints a number n in base b.
* We don't use recursion to avoid deep kernel stacks.
*/
printn(n, b)
register u_long n;
register short b;
{
char prbuf[11];
register char *cp;
if (b == 10 && (int)n < 0) {
putchar('-');
n = (unsigned)(-(int)n);
}
cp = prbuf;
do {
*cp++ = "0123456789abcdef"[n%b];
n /= b;
} while (n);
do
putchar(*--cp);
while (cp > prbuf);
}
/*
* Print a character on console.
*/
putchar(c)
int c;
{
/* XXX - OPENPROMS?? */
#if defined (sun4c) || defined (sun4m)
if (c == '\n')
prom_putchar('\r');
#endif
prom_putchar(c);
}
/*
* Fake getpagesize() system call
*/
getpagesize()
{
return (NBPG);
}
/*
* Fake gettimeofday call
* Needed for ctime - we are lazy and just
* give a bogus approximate answer
*/
gettimeofday(tp, tzp)
struct timeval *tp;
struct timezone *tzp;
{
tp->tv_sec = (1989 - 1970) * 365 * 24 * 60 * 60; /* ~1989 */
tzp->tz_minuteswest = 8 * 60; /* PDT: California ueber alles */
tzp->tz_dsttime = DST_USA;
}
int errno;
caddr_t
sbrk(incr)
int incr;
{
extern char end[];
static caddr_t lim;
caddr_t val;
register int i;
#ifdef sun3x
union {
struct pte pte;
int ipte;
} tmppte;
#endif sun3x
#ifdef sun4m
union ptes tmppte;
#endif sun4m
#ifdef OPENPROMS
register int size;
register caddr_t addr;
#endif OPENPROMS
if (nobrk) {
printf("sbrk: late call\n");
errno = ENOMEM;
return ((caddr_t)-1);
}
if (lim == 0) {
lim = (caddr_t)roundup((u_int)end, NBPG);
#ifdef OPENPROMS
/*
* Workaround for prom_alloc() call in machdep.c
* to allocate the page for L1PTs.
*/
lim += 0x1000;
#endif OPENPROMS
}
incr = btoc(incr);
#ifdef OPENPROMS
size = ctob(incr);
#endif OPENPROMS
if ((lim + ctob(incr)) >= (caddr_t)DEBUGEND) {
printf("sbrk: lim %x + %x exceeds %x\n", lim,
ctob(incr), DEBUGEND);
errno = EINVAL;
return ((caddr_t)-1);
}
val = lim;
pagesused += incr;
#ifdef OPENPROMS
#define BETA_PROM_BUG_FIXED
#ifdef BETA_PROM_BUG_FIXED
if (romp->op_romvec_version > 0) {
lim += size;
addr = (caddr_t)prom_alloc(val, size);
if (addr != val) {
errno = EINVAL;
return ((caddr_t)-1);
}
return (val);
}
#else BETA_PROM_BUG_FIXED
/*
* XXX - A bounds-checking bug in the alloc() routine can
* cause the invalid pmeg to get allocated pages.
* Make life easier for the prom by allocating one page at a time.
* This can go away when the V.2 Open Prom ships.
* Putting this code in for Galaxy here, even though not sure
* if we have the above mentioned bug in our code. Will check on.
*/
if (romp->op_romvec_version > 0) {
caddr_t a;
lim += size;
for (a = val; a < val + size; a += PAGESIZE) {
addr = (caddr_t)prom_alloc(a, PAGESIZE);
if (addr != a) {
errno = EINVAL;
return ((caddr_t)-1);
}
}
return (val);
}
#endif BETA_PROM_BUG_FIXED
#endif OPENPROMS
for (i = 0; i < incr; i++, lim += NBPG) {
#ifdef sun3x
tmppte.ipte = MMU_INVALIDPTE;
tmppte.pte.pte_vld = PTE_VALID;
tmppte.pte.pte_pfn = --lastpg;
Setpgmap(lim, tmppte.ipte);
atc_flush();
#elif sun4m
tmppte.pte_int = PTEOF(0, --lastpg, MMU_STD_SRWX, 0);
Setpgmap(lim, tmppte.pte_int);
mmu_flushall();
#else /* !sun3x && !sun4m */
if (getsegmap(lim) == PMGRP_INVALID) {
register int j = (int)lim & ~PMGRPOFFSET;
int last = j + NPMENTPERPMGRP * NBPG;
setsegmap(lim, --lastpm);
for (; j < last; j += NBPG)
Setpgmap(j, 0);
}
Setpgmap(lim, PG_V | PG_KW | PGT_OBMEM | getapage());
#endif
}
return (val);
}
/*
* Fake ptrace - ignores pid and signals
* Otherwise it's about the same except the "child" never runs,
* flags are just set here to control action elsewhere.
*/
ptrace(request, pid, addr, data, addr2)
enum ptracereq request;
char *addr, *addr2;
{
int rv = 0;
register int i, val;
register int *p;
#ifdef sparc
extern struct allregs adb_regs;
#endif
switch (request) {
case PTRACE_TRACEME: /* do nothing */
break;
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
#ifdef sparc
/*
* Use two peek's instead of a peekl to handle
* misaligned word transfers on sparc.
*/
rv = peek(addr);
if (errno) {
errno = EFAULT;
} else {
/*
* Now read the 2nd half in sparc word wording fashion
*/
int tmp;
tmp = peek(addr + sizeof (short));
if (errno) {
rv = -1;
errno = EFAULT;
} else {
rv = (rv << (sizeof (short) * NBBY)) |
(tmp & 0xffff);
}
}
#else sparc
rv = peekl(addr);
#endif sparc
break;
case PTRACE_PEEKUSER:
i = (int)addr;
if (i < 0 || i > (sizeof(struct user) - sizeof(int))) {
rv = -1;
errno = EIO;
} else {
i = (i + sizeof(int) - 1) & ~(sizeof(int) - 1);
p = (int *)((int)uunix + i);
rv = peekl(p);
}
break;
case PTRACE_POKEUSER:
i = (int)addr;
if (i < 0 || i > (sizeof(struct user) - sizeof(int))) {
rv = -1;
errno = EIO;
} else {
i = (i + sizeof(int) - 1) & ~(sizeof(int) - 1);
p = (int *)((int)uunix + i);
rv = pokel(p, data);
}
break;
case PTRACE_POKETEXT:
rv = poketext(addr, data);
break;
case PTRACE_POKEDATA:
rv = pokel(addr, data);
break;
case PTRACE_SINGLESTEP:
dotrace = 1;
/* fall through to ... */
case PTRACE_CONT:
dorun = 1;
if ((int)addr != 1)
reg->r_pc = (int)addr;
break;
case PTRACE_SETREGS:
#ifdef sparc
rv = scopy(addr, (caddr_t)reg, sizeof (struct allregs));
#else
rv = scopy(addr, (caddr_t)reg, sizeof (struct regs));
#endif
break;
case PTRACE_GETREGS:
#ifdef sparc
rv = scopy((caddr_t)reg, addr, sizeof (struct allregs));
#else
rv = scopy((caddr_t)reg, addr, sizeof (struct regs));
#endif
break;
case PTRACE_WRITETEXT:
case PTRACE_WRITEDATA:
rv = scopy(addr2, addr, data);
break;
case PTRACE_READTEXT:
case PTRACE_READDATA:
rv = scopy(addr, addr2, data);
break;
case PTRACE_KILL:
case PTRACE_ATTACH:
case PTRACE_DETACH:
default:
errno = EINVAL;
rv = -1;
break;
}
return (rv);
}
/*
* This localtime is a modified version of offtime from libc, which does not
* bother to figure out the time zone from the kernel, from environment
* varaibles, or from Unix files. We just return things in GMT format.
*/
static int mon_lengths[2][MONS_PER_YEAR] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static int year_lengths[2] = {
DAYS_PER_NYEAR, DAYS_PER_LYEAR
};
struct tm *
localtime(clock)
time_t *clock;
{
register struct tm *tmp;
register long days;
register long rem;
register int y;
register int yleap;
register int *ip;
static struct tm tm;
tmp = &tm;
days = *clock / SECS_PER_DAY;
rem = *clock % SECS_PER_DAY;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
rem = rem % SECS_PER_HOUR;
tmp->tm_min = (int) (rem / SECS_PER_MIN);
tmp->tm_sec = (int) (rem % SECS_PER_MIN);
tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
if (tmp->tm_wday < 0)
tmp->tm_wday += DAYS_PER_WEEK;
y = EPOCH_YEAR;
if (days >= 0)
for ( ; ; ) {
yleap = isleap(y);
if (days < (long) year_lengths[yleap])
break;
++y;
days = days - (long) year_lengths[yleap];
}
else do {
--y;
yleap = isleap(y);
days = days + (long) year_lengths[yleap];
} while (days < 0);
tmp->tm_year = y - TM_YEAR_BASE;
tmp->tm_yday = (int) days;
ip = mon_lengths[yleap];
for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
days = days - (long) ip[tmp->tm_mon];
tmp->tm_mday = (int) (days + 1);
tmp->tm_isdst = 0;
tmp->tm_zone = "GMT";
tmp->tm_gmtoff = 0;
return (tmp);
}