summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/solbourne/dev/obio.c142
-rw-r--r--sys/arch/solbourne/dev/tod.c236
-rw-r--r--sys/arch/solbourne/dev/todreg.h72
-rw-r--r--sys/arch/solbourne/dev/zsclock.c193
-rw-r--r--sys/arch/solbourne/solbourne/genassym.cf106
-rw-r--r--sys/arch/solbourne/solbourne/locore.s5110
6 files changed, 5859 insertions, 0 deletions
diff --git a/sys/arch/solbourne/dev/obio.c b/sys/arch/solbourne/dev/obio.c
new file mode 100644
index 00000000000..99f1ce5d906
--- /dev/null
+++ b/sys/arch/solbourne/dev/obio.c
@@ -0,0 +1,142 @@
+/* $OpenBSD: obio.c,v 1.1 2005/04/20 01:00:16 miod Exp $ */
+/* OpenBSD: obio.c,v 1.16 2004/09/29 07:35:11 miod Exp */
+
+/*
+ * Copyright (c) 1993, 1994 Theo de Raadt
+ * Copyright (c) 1995, 1997 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+#include <machine/idt.h>
+
+/* autoconfiguration driver */
+static int obiomatch(struct device *, void *, void *);
+static void obioattach(struct device *, struct device *, void *);
+
+int obioprint(void *, const char *);
+int obio_scan(struct device *, void *, void *);
+
+struct cfattach obio_ca = {
+ sizeof(struct device), obiomatch, obioattach
+};
+
+struct cfdriver obio_cd = {
+ NULL, "obio", DV_DULL
+};
+
+/*
+ * A list of the on-board devices in the IDT systems. This is better than
+ * having people playing with locators in their kernel configuration
+ * files, and necessary because the device tree built by the PROM does not
+ * list all on-board devices (audio and floppy are missing).
+ */
+const struct {
+ char *devname;
+ paddr_t address;
+ int intr;
+} obio_devices[] = {
+ { "tod", TODCLOCK_BASE, -1 },
+ { "nvram", NVRAM_BASE, -1 },
+ { "zs", ZS0_BASE, 12 },
+ { "zs", ZS1_BASE, 12 },
+ { "fdc", FDC_BASE, 11 },
+ { "audioamd", AUDIO_BASE, 13 },
+ { "wdsc", SE_BASE + 0x20, 4 },
+ { "le", SE_BASE + 0x30, 6 },
+ { NULL, 0 }
+};
+
+int
+obiomatch(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ register struct cfdata *cf = vcf;
+ register struct confargs *ca = aux;
+ register struct romaux *ra = &ca->ca_ra;
+
+ return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
+}
+
+int
+obioprint(args, obio)
+ void *args;
+ const char *obio;
+{
+ register struct confargs *ca = args;
+
+ if (ca->ca_ra.ra_name == NULL)
+ ca->ca_ra.ra_name = "<unknown>";
+
+ if (obio)
+ printf("%s at %s", ca->ca_ra.ra_name, obio);
+
+ printf(" addr %p", ca->ca_ra.ra_paddr);
+
+ return (UNCONF);
+}
+
+void
+obioattach(parent, self, args)
+ struct device *parent, *self;
+ void *args;
+{
+ struct confargs *ca = args;
+ struct confargs oca;
+ int i;
+
+ if (self->dv_unit > 0) {
+ printf(" unsupported\n");
+ return;
+ }
+ printf("\n");
+
+ for (i = 0; obio_devices[i].devname != NULL; i++) {
+ /* fake a rom_reg */
+ bzero(&oca, sizeof oca);
+ oca.ca_ra.ra_paddr = (caddr_t)obio_devices[i].address;
+ oca.ca_ra.ra_vaddr = NULL;
+ oca.ca_ra.ra_len = 0;
+ oca.ca_ra.ra_nreg = 1;
+ oca.ca_ra.ra_iospace = 0;
+ oca.ca_ra.ra_intr[0].int_pri = obio_devices[i].intr;
+ oca.ca_ra.ra_intr[0].int_vec = -1;
+ oca.ca_ra.ra_nintr = oca.ca_ra.ra_intr[0].int_pri < 0 ? 0 : 1;
+ oca.ca_ra.ra_name = obio_devices[i].devname;
+ if (ca->ca_ra.ra_bp != NULL)
+ oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1;
+ else
+ oca.ca_ra.ra_bp = NULL;
+ oca.ca_bustype = BUS_OBIO;
+
+ config_found(self, &oca, obioprint);
+ }
+}
diff --git a/sys/arch/solbourne/dev/tod.c b/sys/arch/solbourne/dev/tod.c
new file mode 100644
index 00000000000..86f8503bb51
--- /dev/null
+++ b/sys/arch/solbourne/dev/tod.c
@@ -0,0 +1,236 @@
+/* $OpenBSD: tod.c,v 1.1 2005/04/20 01:00:16 miod Exp $ */
+/*
+ * Copyright (c) 2005, Miodrag Vallat
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * TODclock driver. We only use it to know the current time during boot,
+ * as we do not get interrupts from it.
+ *
+ * The clock in the IDT machines is the Oki MSM62X42BRS.
+ *
+ * A datasheet for this chip is available from:
+ * http://www.datasheetarchive.com/datasheet/pdf/19/196099.html
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+
+#include <solbourne/dev/todreg.h>
+#include <dev/clock_subr.h>
+
+#include <machine/idt.h>
+#include <machine/kap.h>
+
+int todmatch(struct device *, void *, void *);
+void todattach(struct device *, struct device *, void *);
+
+struct cfattach tod_ca = {
+ sizeof(struct device), todmatch, todattach
+};
+
+struct cfdriver tod_cd = {
+ NULL, "tod", DV_DULL
+};
+
+volatile u_char *tod_regs;
+
+u_char msm_read(u_int);
+void msm_write(u_int, u_char);
+
+int
+todmatch(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct confargs *ca = aux;
+
+ return (strcmp(tod_cd.cd_name, ca->ca_ra.ra_name) == 0);
+}
+
+void
+todattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ printf(": OKI MSM62X42BRS\n");
+
+ /* the register are already mapped 1:1 by pmap_bootstrap() */
+ tod_regs = (volatile u_char *)TODCLOCK_BASE;
+}
+
+/*
+ * Read or write a register of the Oki clock.
+ *
+ * The clock registers are not directly accessible (while control registers
+ * are). We need to freeze them first. To do so, we set the hold bit in
+ * D, and if the busy bit clears, we are free to proceed. If the busy bit
+ * is still set, we need to clear the hold bit and retry.
+ */
+u_char
+msm_read(u_int regno)
+{
+ u_char d, r;
+
+ /* no need to do the hold dance for control registers */
+ if (regno >= MSM_D)
+ return (tod_regs[regno] & 0x0f);
+
+ d = tod_regs[MSM_D] & 0x0f & ~MSM_D_HOLD;
+ for (;;) {
+ tod_regs[MSM_D] = d | MSM_D_HOLD;
+ if (!ISSET(tod_regs[MSM_D], MSM_D_BUSY))
+ break;
+ tod_regs[MSM_D] = d;
+ }
+
+ r = tod_regs[regno] & 0x0f;
+ tod_regs[MSM_D] = d;
+
+ return (r);
+}
+
+void
+msm_write(u_int regno, u_char value)
+{
+ u_char d;
+
+ /* no need to do the hold dance for control registers */
+ if (regno >= MSM_D) {
+ tod_regs[regno] = value;
+ return;
+ }
+
+ d = tod_regs[MSM_D] & 0x0f & ~MSM_D_HOLD;
+ for (;;) {
+ tod_regs[MSM_D] = d | MSM_D_HOLD;
+ if (!ISSET(tod_regs[MSM_D], MSM_D_BUSY))
+ break;
+ tod_regs[MSM_D] = d;
+ }
+
+ tod_regs[regno] = value;
+ tod_regs[MSM_D] = d;
+}
+
+void
+inittodr(base)
+ time_t base;
+{
+ struct clock_ymdhms dt;
+
+ dt.dt_sec = msm_read(MSM_SEC_UNITS) + 10 * msm_read(MSM_SEC_TENS);
+ dt.dt_min = msm_read(MSM_MIN_UNITS) + 10 * msm_read(MSM_MIN_TENS);
+#if 0
+ dt.dt_hour = msm_read(MSM_HOUR_UNITS) + 10 * msm_read(MSM_HOUR_TENS);
+#else
+ dt.dt_hour = msm_read(MSM_HOUR_TENS);
+ if (dt.dt_hour & MSM_HOUR_PM)
+ dt.dt_hour = 12 + 10 * (dt.dt_hour & ~MSM_HOUR_TENS);
+ else
+ dt.dt_hour *= 10;
+ dt.dt_hour += msm_read(MSM_HOUR_UNITS);
+#endif
+ dt.dt_day = msm_read(MSM_DAY_UNITS) + 10 * msm_read(MSM_DAY_TENS);
+ dt.dt_mon = msm_read(MSM_MONTH_UNITS) + 10 * msm_read(MSM_MONTH_TENS);
+ dt.dt_year = msm_read(MSM_YEAR_UNITS) + 10 * msm_read(MSM_YEAR_TENS);
+ dt.dt_year += CLOCK_YEAR_BASE;
+ /* dt_wday left uninitialized */
+
+ time.tv_sec = clock_ymdhms_to_secs(&dt);
+
+ if (time.tv_sec == 0) {
+ /*
+ * Believe the time in the file system for lack of
+ * anything better, resetting the clock.
+ */
+ if (base < 35 * SECYR) {/* this port did not exist until 2005 */
+ /*
+ * If base is 0, assume filesystem time is just unknown
+ * in stead of preposterous. Don't bark.
+ */
+ if (base != 0)
+ printf("WARNING: preposterous time in file system\n");
+ /* not going to use it anyway, if the chip is readable */
+ time.tv_sec = 35 * SECYR + 90 * SECDAY + SECDAY / 2;
+ } else {
+ printf("WARNING: bad date in battery clock");
+ time.tv_sec = base;
+ resettodr();
+ }
+ } else {
+ int deltat = time.tv_sec - base;
+
+ if (deltat < 0)
+ deltat = -deltat;
+ if (deltat < 2 * SECDAY)
+ return;
+
+#ifndef SMALL_KERNEL
+ printf("WARNING: clock %s %d days",
+ time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
+#endif
+ }
+ printf(" -- CHECK AND RESET THE DATE!\n");
+}
+
+void
+resettodr()
+{
+ struct clock_ymdhms dt;
+
+ if (time.tv_sec == 0 || tod_regs == NULL)
+ return;
+
+ clock_secs_to_ymdhms(time.tv_sec, &dt);
+
+ /*
+ * Since we don't know if the clock is in AM/PM or 24 hour mode,
+ * we need to reset it and force one mode. Being an evil european
+ * person, I'll force 24 hour mode, of course.
+ */
+ msm_write(MSM_F, MSM_F_RESET | MSM_F_24HR);
+ msm_write(MSM_F, MSM_F_STOP); /* leave reset mode, but stop clock */
+
+ dt.dt_year -= CLOCK_YEAR_BASE;
+ msm_write(MSM_YEAR_TENS, dt.dt_year / 10);
+ msm_write(MSM_YEAR_UNITS, dt.dt_year % 10);
+ msm_write(MSM_MONTH_TENS, dt.dt_mon / 10);
+ msm_write(MSM_MONTH_UNITS, dt.dt_mon % 10);
+ msm_write(MSM_DAY_TENS, dt.dt_day / 10);
+ msm_write(MSM_DAY_UNITS, dt.dt_day % 10);
+ msm_write(MSM_HOUR_TENS, dt.dt_hour / 10);
+ msm_write(MSM_HOUR_UNITS, dt.dt_hour % 10);
+ msm_write(MSM_MIN_TENS, dt.dt_min / 10);
+ msm_write(MSM_MIN_UNITS, dt.dt_min % 10);
+ msm_write(MSM_SEC_TENS, dt.dt_sec / 10);
+ msm_write(MSM_SEC_UNITS, dt.dt_sec % 10);
+
+ msm_write(MSM_F, 0); /* restart clock */
+}
diff --git a/sys/arch/solbourne/dev/todreg.h b/sys/arch/solbourne/dev/todreg.h
new file mode 100644
index 00000000000..540fdf2f06f
--- /dev/null
+++ b/sys/arch/solbourne/dev/todreg.h
@@ -0,0 +1,72 @@
+/* $OpenBSD: todreg.h,v 1.1 2005/04/20 01:00:16 miod Exp $ */
+/*
+ * Copyright (c) 2005, Miodrag Vallat
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Oki MSM62X42BRS registers.
+ *
+ * A datasheet for this chip is available from:
+ * http://www.datasheetarchive.com/datasheet/pdf/19/196099.html
+ */
+
+#define MSM_REG(x) ((x) << 3)
+
+#define MSM_SEC_UNITS MSM_REG(0) /* seconds, low digit */
+#define MSM_SEC_TENS MSM_REG(1) /* seconds, high digit */
+#define MSM_MIN_UNITS MSM_REG(2) /* minutes, low digit */
+#define MSM_MIN_TENS MSM_REG(3) /* minutes, high digit */
+#define MSM_HOUR_UNITS MSM_REG(4) /* hours, low digit */
+#define MSM_HOUR_TENS MSM_REG(5) /* hours, high digit */
+#define MSM_HOUR_PM 0x04 /* PM bit if PM mode */
+#define MSM_DAY_UNITS MSM_REG(6) /* day, low digit */
+#define MSM_DAY_TENS MSM_REG(7) /* day, high digit */
+#define MSM_MONTH_UNITS MSM_REG(8) /* month, low digit */
+#define MSM_MONTH_TENS MSM_REG(9) /* month, high digit */
+#define MSM_YEAR_UNITS MSM_REG(10) /* year, low digit */
+#define MSM_YEAR_TENS MSM_REG(11) /* year, high digit */
+#define CLOCK_YEAR_BASE 1968
+#define MSM_DOW MSM_REG(12) /* day of week, 0 = sunday */
+
+#define MSM_D MSM_REG(13) /* control register D */
+#define MSM_D_HOLD 0x01 /* hold clock for access */
+#define MSM_D_BUSY 0x02 /* clock is busy */
+#define MSM_D_INTR 0x04 /* interrupt pending */
+#define MSM_D_30 0x08 /* 30 seconds adjustment */
+
+#define MSM_E MSM_REG(14) /* control register E */
+#define MSM_E_MASK 0x01 /* output mask (set to disable) */
+#define MSM_E_INTR 0x02 /* output interrupts (1) or pulse (0) */
+#define MSM_E_PERIOD 0x04 /* interrupt (or pulse) period - needs
+ to be written twice! (2 bits) */
+#define MSM_PERIOD_64HZ 0x00
+#define MSM_PERIOD_1HZ 0x01
+#define MSM_PERIOD_1MIN 0x10
+#define MSM_PERIOD_1HOUR 0x11
+
+#define MSM_F MSM_REG(15) /* control register F */
+#define MSM_F_RESET 0x01 /* reset clock */
+#define MSM_F_STOP 0x02 /* stop clock */
+#define MSM_F_24HR 0x04 /* 24 hour mode (valid on reset only) */
+#define MSM_F_TEST 0x08 /* assert test signal */
diff --git a/sys/arch/solbourne/dev/zsclock.c b/sys/arch/solbourne/dev/zsclock.c
new file mode 100644
index 00000000000..e225d58dcf2
--- /dev/null
+++ b/sys/arch/solbourne/dev/zsclock.c
@@ -0,0 +1,193 @@
+/* $OpenBSD: zsclock.c,v 1.1 2005/04/20 01:00:16 miod Exp $ */
+/* $NetBSD: clock.c,v 1.11 1995/05/16 07:30:46 phil Exp $ */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clock.c 7.2 (Berkeley) 5/12/91
+ *
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ *
+ * Improved by Phil Budne ... 10/17/94.
+ * Pulled over code from i386/isa/clock.c (Matthias Pfaller 12/03/94).
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/kernel.h>
+
+#include <sparc/dev/z8530reg.h>
+#include <machine/z8530var.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+
+/*
+ * Zilog Z8530 Dual UART driver (clock interface)
+ */
+
+/* Clock state. */
+struct zsclock_softc {
+ struct device zsc_dev;
+ struct zs_chanstate *zsc_cs;
+};
+
+int zsclock_match(struct device *, void *, void *);
+void zsclock_attach(struct device *, struct device *, void *);
+
+struct cfattach zsclock_ca = {
+ sizeof(struct zsclock_softc), zsclock_match, zsclock_attach
+};
+
+struct cfdriver zsclock_cd = {
+ NULL, "zsclock", DV_DULL
+};
+
+void zsclock_stint(struct zs_chanstate *, int);
+
+struct zsops zsops_clock = {
+ NULL,
+ zsclock_stint,
+ NULL,
+ NULL
+};
+
+static int zsclock_attached;
+
+/*
+ * clock_match: how is this zs channel configured?
+ */
+int
+zsclock_match(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+ struct zsc_attach_args *args = aux;
+
+ /* only attach one instance */
+ if (zsclock_attached)
+ return (0);
+
+ /* make sure we'll win a probe over zstty or zskbd */
+ if (cf->cf_loc[ZSCCF_CHANNEL] == args->channel)
+ return (2 + 2);
+
+ if (cf->cf_loc[ZSCCF_CHANNEL] == ZSCCF_CHANNEL_DEFAULT)
+ return (2 + 1);
+
+ return (0);
+}
+
+/*
+ * The Solbourne IDT systems provide a 4.9152 MHz clock to the ZS chips.
+ */
+#define PCLK (9600 * 512) /* PCLK pin input clock rate */
+
+void
+zsclock_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct zsc_softc *zsc = (void *)parent;
+ struct zsclock_softc *sc = (void *)self;
+ struct zsc_attach_args *args = aux;
+ struct zs_chanstate *cs;
+ int channel;
+ int reset, s, tconst;
+
+ channel = args->channel;
+
+ cs = &zsc->zsc_cs[channel];
+ cs->cs_private = zsc;
+ cs->cs_ops = &zsops_clock;
+
+ sc->zsc_cs = cs;
+
+ printf("\n");
+
+ hz = 100;
+ tconst = ((PCLK / 2) / hz) - 2
+
+ s = splclock();
+
+ reset = (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET;
+ zs_write_reg(cs, 9, reset);
+
+ cs->cs_preg[1] = 0;
+ cs->cs_preg[3] = ZSWR3_RX_8 | ZSWR3_RX_ENABLE;
+ cs->cs_preg[4] = ZSWR4_CLK_X1 | ZSWR4_ONESB | ZSWR4_PARENB;
+ cs->cs_preg[5] = ZSWR5_TX_8 | ZSWR5_TX_ENABLE;
+ cs->cs_preg[9] = ZSWR9_MASTER_IE;
+ cs->cs_preg[10] = 0;
+ cs->cs_preg[11] = ZSWR11_RXCLK_RTXC | ZSWR11_TXCLK_RTXC |
+ ZSWR11_TRXC_OUT_ENA | ZSWR11_TRXC_BAUD;
+ cs->cs_preg[12] = tconst;
+ cs->cs_preg[13] = tconst >> 8;
+ cs->cs_preg[14] = ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA;
+ cs->cs_preg[15] = ZSWR15_ZERO_COUNT_IE;
+
+ zs_loadchannelregs(cs);
+
+ splx(s);
+
+ /* enable interrupts */
+ cs->cs_preg[1] |= ZSWR1_SIE;
+ zs_write_reg(cs, 1, cs->cs_preg[1]);
+
+ zsclock_attached = 1;
+}
+
+void
+zsclock_stint(struct zs_chanstate *cs, int force)
+{
+ u_char rr0;
+
+ rr0 = zs_read_csr(cs);
+ cs->cs_rr0 = rr0;
+
+ /*
+ * Retrigger the interrupt as a soft interrupt, because we need
+ * a trap frame for hardclock().
+ */
+ ienab_bis(IE_L10);
+
+ zs_write_csr(cs, ZSWR0_RESET_STATUS);
+}
diff --git a/sys/arch/solbourne/solbourne/genassym.cf b/sys/arch/solbourne/solbourne/genassym.cf
new file mode 100644
index 00000000000..d2774076a97
--- /dev/null
+++ b/sys/arch/solbourne/solbourne/genassym.cf
@@ -0,0 +1,106 @@
+# $OpenBSD: genassym.cf,v 1.1 2005/04/20 01:00:18 miod Exp $
+#
+# Copyright (c) 2005, Miodrag Vallat
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+include <sys/param.h>
+include <sys/syscall.h>
+include <sys/user.h>
+
+include <uvm/uvm_extern.h>
+
+include <machine/cpu.h>
+include <machine/frame.h>
+include <machine/kap.h>
+include <machine/pmap.h>
+include <machine/prom.h>
+
+include <sparc/sparc/cpuvar.h>
+
+#
+# Solbourne PROM specific constants
+#
+
+struct frame
+member FRAME_ARGC fr_argx
+
+export PROM_DATA_PA
+
+struct sb_prom SB_
+member REGS sp_regs
+
+#
+# Sparc general constants and structures
+#
+
+export USRSTACK
+export VM_MIN_KERNEL_ADDRESS
+
+export SYS_exit
+export SYS_sigreturn
+
+export EFAULT
+export ENAMETOOLONG
+
+struct fpstate
+member fs_fsr
+member fs_qsize
+member fs_queue
+member fs_regs
+export FSR_QNE
+
+# intrhand fields.
+struct intrhand
+member ih_arg
+member IH_COUNT ih_count.ec_count
+member ih_fun
+member ih_ipl
+member ih_next
+
+struct pcb
+member pcb_nsaved
+member pcb_onfault
+member pcb_psr
+member pcb_rw
+member pcb_sp
+member pcb_uw
+member pcb_wim
+member pcb_wcookie
+
+struct pmap
+member PMAP_PSEGTAB pm_psegtab
+define VM_PMAP offsetof(struct vmspace, vm_map.pmap)
+
+struct proc
+member p_addr
+member p_stat
+member p_vmspace
+member p_wchan
+
+export SRUN
+export SONPROC
+
+struct uvmexp V_
+member INTR intrs
+member swtch
diff --git a/sys/arch/solbourne/solbourne/locore.s b/sys/arch/solbourne/solbourne/locore.s
new file mode 100644
index 00000000000..0cd70db34d8
--- /dev/null
+++ b/sys/arch/solbourne/solbourne/locore.s
@@ -0,0 +1,5110 @@
+/* $OpenBSD: locore.s,v 1.1 2005/04/20 01:00:18 miod Exp $ */
+/* OpenBSD: locore.s,v 1.64 2005/04/17 18:47:50 miod Exp */
+
+/*
+ * Copyright (c) 2005, Miodrag Vallat
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1996 Paul Kranenburg
+ * Copyright (c) 1996
+ * The President and Fellows of Harvard College. All rights reserved.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * This product includes software developed by Harvard University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * This product includes software developed by Harvard University.
+ * This product includes software developed by Paul Kranenburg.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)locore.s 8.4 (Berkeley) 12/10/93
+ */
+
+#include "assym.h"
+#include "ksyms.h"
+
+#include <machine/param.h>
+#include <machine/asm.h>
+#include <machine/idt.h>
+#include <machine/kap.h>
+#include <machine/pte.h>
+
+#include <sparc/sparc/intreg.h>
+
+#include <machine/psl.h>
+#include <machine/pte.h>
+#include <machine/signal.h>
+#include <machine/trap.h>
+#ifdef COMPAT_SVR4
+#include <compat/svr4/svr4_syscall.h>
+#endif
+
+#include <sparc/dev/z8530reg.h>
+
+/*
+ * GNU assembler does not understand `.empty' directive; Sun assembler
+ * gripes about labels without it. To allow cross-compilation using
+ * the Sun assembler, and because .empty directives are useful documentation,
+ * we use this trick.
+ */
+#ifdef SUN_AS
+#define EMPTY .empty
+#else
+#define EMPTY /* .empty */
+#endif
+
+/* use as needed to align things on longword boundaries */
+#define _ALIGN .align 4
+
+/*
+ * CCFSZ (C Compiler Frame SiZe) is the size of a stack frame required if
+ * a function is to call C code. It should be just 64, but Sun defined
+ * their frame with space to hold arguments 0 through 5 (plus some junk),
+ * and varargs routines (such as printf) demand this, and gcc uses this
+ * area at times anyway.
+ */
+#define CCFSZ 96
+
+/*
+ * A handy macro for maintaining instrumentation counters.
+ * Note that this clobbers %o0 and %o1. Normal usage is
+ * something like:
+ * foointr:
+ * TRAP_SETUP(...) ! makes %o registers safe
+ * INCR(_C_LABEL(cnt)+V_FOO) ! count a foo
+ */
+#define INCR(what) \
+ sethi %hi(what), %o0; \
+ ld [%o0 + %lo(what)], %o1; \
+ inc %o1; \
+ st %o1, [%o0 + %lo(what)]
+
+/*
+ * Another handy macro: load one register window, given `base' address.
+ * This can be either a simple register (e.g., %sp) or include an initial
+ * offset (e.g., %g6 + PCB_RW).
+ */
+#define LOADWIN(addr) \
+ ldd [addr], %l0; \
+ ldd [addr + 8], %l2; \
+ ldd [addr + 16], %l4; \
+ ldd [addr + 24], %l6; \
+ ldd [addr + 32], %i0; \
+ ldd [addr + 40], %i2; \
+ ldd [addr + 48], %i4; \
+ ldd [addr + 56], %i6
+
+/*
+ * To return from trap we need the two-instruction sequence
+ * `jmp %l1; rett %l2', which is defined here for convenience.
+ */
+#define RETT jmp %l1; rett %l2
+
+/*
+ * On some KAP models, the FCR register is not cleared after being read,
+ * or when being written to by the hardware, so we have to clear it by
+ * ourselves after every read.
+ */
+#define CLEAR_FCR \
+ sta %g0, [%g0] ASI_FCR
+
+ .data
+/*
+ * The interrupt stack.
+ *
+ * This is the very first thing in the data segment, and therefore has
+ * the lowest kernel stack address. We count on this in the interrupt
+ * trap-frame setup code, since we may need to switch from the kernel
+ * stack to the interrupt stack (iff we are not already on the interrupt
+ * stack). One sethi+cmp is all we need since this is so carefully
+ * arranged.
+ */
+ .globl _C_LABEL(intstack)
+ .globl _C_LABEL(eintstack)
+_C_LABEL(intstack):
+ .skip 128 * 128 ! 16k = 128 128-byte stack frames
+_C_LABEL(eintstack):
+
+/*
+ * When a process exits and its u. area goes away, we set cpcb to point
+ * to this `u.', leaving us with something to use for an interrupt stack,
+ * and letting all the register save code have a pcb_uw to examine.
+ * This is also carefully arranged (to come just before u0, so that
+ * process 0's kernel stack can quietly overrun into it during bootup, if
+ * we feel like doing that).
+ */
+ .globl _C_LABEL(idle_u)
+_C_LABEL(idle_u):
+ .skip USPACE
+
+/*
+ * Process 0's u.
+ *
+ * This must be aligned on an 8 byte boundary.
+ */
+ .globl _C_LABEL(u0)
+_C_LABEL(u0): .skip USPACE
+estack0:
+
+#ifdef KGDB
+/*
+ * Another item that must be aligned, easiest to put it here.
+ */
+KGDB_STACK_SIZE = 2048
+ .globl _C_LABEL(kgdb_stack)
+_C_LABEL(kgdb_stack):
+ .skip KGDB_STACK_SIZE ! hope this is enough
+#endif
+
+/*
+ * cpcb points to the current pcb (and hence u. area).
+ * Initially this is the special one.
+ */
+ .globl _C_LABEL(cpcb)
+_C_LABEL(cpcb): .word _C_LABEL(u0)
+
+/*
+ * cputyp is the current cpu type, used to distinguish between
+ * the many variations of different sun4* machines. It contains
+ * the value CPU_SUN4, CPU_SUN4C, or CPU_SUN4M.
+ */
+ .globl _C_LABEL(cputyp)
+_C_LABEL(cputyp):
+ .word CPU_KAP
+/*
+ * cpumod is the current cpu model, used to distinguish between variants
+ * in the Sun4 and Sun4M families. See /sys/arch/sparc/include/param.h for
+ * possible values.
+ */
+ .globl _C_LABEL(cpumod)
+_C_LABEL(cpumod):
+ .word 1
+
+/*
+ * nbpg is used by pmap_bootstrap(), pgofset is used internally.
+ */
+ .globl _C_LABEL(nbpg)
+_C_LABEL(nbpg):
+ .word PAGE_SIZE
+_C_LABEL(pgofset):
+ .word PAGE_MASK
+
+ .globl _C_LABEL(trapbase)
+_C_LABEL(trapbase):
+ .word _C_LABEL(trapbase_kap)
+
+ _ALIGN
+
+ .text
+
+/*
+ * The first thing in the real text segment is the trap vector table,
+ * which must be aligned on a page boundary. The text segment
+ * starts beyond page 0 of KERNBASE so that there is a red zone
+ * between user and kernel space. We place the message buffer in this
+ * area. Because the message buffer is in our "red zone" between user
+ * and kernel space we remap it in configure() to another location and
+ * invalidate the mapping at KERNBASE.
+ */
+
+/*
+ * Each trap has room for four instructions, of which one perforce must
+ * be a branch. On entry the hardware has copied pc and npc to %l1 and
+ * %l2 respectively. We use two more to read the psr into %l0, and to
+ * put the trap type value into %l3 (with a few exceptions below).
+ * We could read the trap type field of %tbr later in the code instead,
+ * but there is no need, and that would require more instructions
+ * (read+mask, vs 1 `mov' here).
+ *
+ * I used to generate these numbers by address arithmetic, but gas's
+ * expression evaluator has about as much sense as your average slug
+ * (oddly enough, the code looks about as slimy too). Thus, all the
+ * trap numbers are given as arguments to the trap macros. This means
+ * there is one line per trap. Sigh.
+ *
+ * Note that only the local registers may be used, since the trap
+ * window is potentially the last window. Its `in' registers are
+ * the previous window's outs (as usual), but more important, its
+ * `out' registers may be in use as the `topmost' window's `in' registers.
+ * The global registers are of course verboten (well, until we save
+ * them away).
+ *
+ * Hardware interrupt vectors can be `linked'---the linkage is to regular
+ * C code---or rewired to fast in-window handlers. The latter are good
+ * for unbuffered hardware like the Zilog serial chip and the AMD audio
+ * chip, where many interrupts can be handled trivially with pseudo-DMA or
+ * similar. Only one `fast' interrupt can be used per level, however, and
+ * direct and `fast' interrupts are incompatible. Routines in intr.c
+ * handle setting these, with optional paranoia.
+ */
+
+ /* regular vectored traps */
+#define VTRAP(type, label) \
+ rd %wim, %l0; mov (type), %l3; b label; mov %psr, %l0
+
+ /* hardware interrupts (can be linked or made `fast') */
+#define HARDINTKAP(lev) \
+ mov (lev), %l3; b sparc_interruptkap; mov %psr, %l0; nop
+
+ /* software interrupts (may not be made direct, sorry---but you
+ should not be using them trivially anyway) */
+#define SOFTINTKAP(lev, bit) \
+ mov (lev), %l3; mov (bit - 1), %l4; b softintr_kap; mov %psr, %l0
+
+ /* traps that just call trap() */
+#define TRAP(type) VTRAP(type, slowtrap)
+
+ /* architecturally undefined traps (cause panic) */
+#define UTRAP(type) VTRAP(type, slowtrap)
+
+ /* software undefined traps (may be replaced) */
+#define STRAP(type) VTRAP(type, slowtrap)
+
+/* breakpoint acts differently under kgdb */
+#ifdef KGDB
+#define BPT VTRAP(T_BREAKPOINT, bpt)
+#define BPT_KGDB_EXEC VTRAP(T_KGDB_EXEC, bpt)
+#else
+#define BPT TRAP(T_BREAKPOINT)
+#define BPT_KGDB_EXEC TRAP(T_KGDB_EXEC)
+#endif
+
+/* special high-speed 1-instruction-shaved-off traps (get nothing in %l3) */
+#define SYSCALL b _C_LABEL(_syscall); mov %psr, %l0; nop; nop
+#define TLBTRAP(label) b label; mov %psr, %l0; nop; nop
+#define WINDOW_OF b window_of; mov %psr, %l0; nop; nop
+#define WINDOW_UF b window_uf; mov %psr, %l0; nop; nop
+#ifdef notyet
+#define ZS_INTERRUPT b zshard; mov %psr, %l0; nop; nop
+#else
+#define ZS_INTERRUPTKAP HARDINTKAP(12)
+#endif
+
+ .globl start, _C_LABEL(kernel_text)
+_C_LABEL(kernel_text):
+start:
+/*
+ * The traptable needs to be aligned on a 4096 bytes boundary on KAP.
+ */
+trapbase_kap:
+/* trap 0 is special since we cannot receive it */
+ b dostart; nop; nop; nop ! 00 = reset (fake)
+ VTRAP(T_TEXTFAULT, mem_fault) ! 01 = instr. fetch fault
+ TRAP(T_ILLINST) ! 02 = illegal instruction
+ TRAP(T_PRIVINST) ! 03 = privileged instruction
+ TRAP(T_FPDISABLED) ! 04 = fp instr, but EF bit off in psr
+ WINDOW_OF ! 05 = window overflow
+ WINDOW_UF ! 06 = window underflow
+ TRAP(T_ALIGN) ! 07 = address alignment error
+ VTRAP(T_FPE, fp_exception) ! 08 = fp exception
+ VTRAP(T_DATAFAULT, mem_fault) ! 09 = data fetch fault
+ TRAP(T_TAGOF) ! 0a = tag overflow
+ UTRAP(0x0b)
+ UTRAP(0x0c)
+ UTRAP(0x0d)
+ UTRAP(0x0e)
+ UTRAP(0x0f)
+ UTRAP(0x10)
+ SOFTINTKAP(1, IE_L1) ! 11 = level 1 interrupt
+ HARDINTKAP(2) ! 12 = level 2 interrupt
+ HARDINTKAP(3) ! 13 = level 3 interrupt
+ SOFTINTKAP(4, IE_L4) ! 14 = level 4 interrupt
+ HARDINTKAP(5) ! 15 = level 5 interrupt
+ SOFTINTKAP(6, IE_L6) ! 16 = level 6 interrupt
+ HARDINTKAP(7) ! 17 = level 7 interrupt
+ HARDINTKAP(8) ! 18 = level 8 interrupt
+ HARDINTKAP(9) ! 19 = level 9 interrupt
+ SOFTINTKAP(10, IE_L10) ! 1a = level 10 interrupt (clock)
+ HARDINTKAP(11) ! 1b = level 11 interrupt
+ ZS_INTERRUPTKAP ! 1c = level 12 (zs) interrupt
+ HARDINTKAP(13) ! 1d = level 13 interrupt
+ HARDINTKAP(14) ! 1e = level 14 interrupt
+ VTRAP(15, nmi) ! 1f = nonmaskable interrupt
+ VTRAP(0x20, double_trap) ! 20 = double trap
+ UTRAP(0x21)
+ UTRAP(0x22)
+ UTRAP(0x23)
+ TRAP(T_CPDISABLED) ! 24 = coprocessor instr, EC bit off in psr
+ UTRAP(0x25)
+ UTRAP(0x26)
+ UTRAP(0x27)
+ TRAP(T_CPEXCEPTION) ! 28 = coprocessor exception
+ UTRAP(0x29)
+ UTRAP(0x2a)
+ UTRAP(0x2b)
+ TLBTRAP(dtlb_miss) ! 2c = dtlb lookup miss
+ UTRAP(0x2d)
+ UTRAP(0x2e)
+ UTRAP(0x2f)
+ UTRAP(0x30)
+ UTRAP(0x31)
+ UTRAP(0x32)
+ UTRAP(0x33)
+ UTRAP(0x34)
+ UTRAP(0x35)
+ UTRAP(0x36)
+ UTRAP(0x37)
+ UTRAP(0x38)
+ UTRAP(0x39)
+ UTRAP(0x3a)
+ UTRAP(0x3b)
+ TLBTRAP(itlb_miss) ! 3c itlb lookup miss
+ UTRAP(0x3d)
+ UTRAP(0x3e)
+ UTRAP(0x3f)
+ UTRAP(0x40)
+ UTRAP(0x41)
+ UTRAP(0x42)
+ UTRAP(0x43)
+ UTRAP(0x44)
+ UTRAP(0x45)
+ UTRAP(0x46)
+ UTRAP(0x47)
+ UTRAP(0x48)
+ UTRAP(0x49)
+ UTRAP(0x4a)
+ UTRAP(0x4b)
+ UTRAP(0x4c)
+ UTRAP(0x4d)
+ UTRAP(0x4e)
+ UTRAP(0x4f)
+ UTRAP(0x50)
+ UTRAP(0x51)
+ UTRAP(0x52)
+ UTRAP(0x53)
+ UTRAP(0x54)
+ UTRAP(0x55)
+ UTRAP(0x56)
+ UTRAP(0x57)
+ UTRAP(0x58)
+ UTRAP(0x59)
+ UTRAP(0x5a)
+ UTRAP(0x5b)
+ UTRAP(0x5c)
+ UTRAP(0x5d)
+ UTRAP(0x5e)
+ UTRAP(0x5f)
+ UTRAP(0x60)
+ UTRAP(0x61)
+ UTRAP(0x62)
+ UTRAP(0x63)
+ UTRAP(0x64)
+ UTRAP(0x65)
+ UTRAP(0x66)
+ UTRAP(0x67)
+ UTRAP(0x68)
+ UTRAP(0x69)
+ UTRAP(0x6a)
+ UTRAP(0x6b)
+ UTRAP(0x6c)
+ UTRAP(0x6d)
+ UTRAP(0x6e)
+ UTRAP(0x6f)
+ UTRAP(0x70)
+ UTRAP(0x71)
+ UTRAP(0x72)
+ UTRAP(0x73)
+ UTRAP(0x74)
+ UTRAP(0x75)
+ UTRAP(0x76)
+ UTRAP(0x77)
+ UTRAP(0x78)
+ UTRAP(0x79)
+ UTRAP(0x7a)
+ UTRAP(0x7b)
+ UTRAP(0x7c)
+ UTRAP(0x7d)
+ UTRAP(0x7e)
+ UTRAP(0x7f)
+ SYSCALL ! 80 = sun syscall
+ BPT ! 81 = pseudo breakpoint instruction
+ TRAP(T_DIV0) ! 82 = divide by zero
+ TRAP(T_FLUSHWIN) ! 83 = flush windows
+ TRAP(T_CLEANWIN) ! 84 = provide clean windows
+ TRAP(T_RANGECHECK) ! 85 = ???
+ TRAP(T_FIXALIGN) ! 86 = fix up unaligned accesses
+ TRAP(T_INTOF) ! 87 = integer overflow
+ SYSCALL ! 88 = svr4 syscall
+ SYSCALL ! 89 = bsd syscall
+ BPT_KGDB_EXEC ! 8a = enter kernel gdb on kernel startup
+ STRAP(0x8b)
+ STRAP(0x8c)
+ STRAP(0x8d)
+ STRAP(0x8e)
+ STRAP(0x8f)
+ STRAP(0x90)
+ STRAP(0x91)
+ STRAP(0x92)
+ STRAP(0x93)
+ STRAP(0x94)
+ STRAP(0x95)
+ STRAP(0x96)
+ STRAP(0x97)
+ STRAP(0x98)
+ STRAP(0x99)
+ STRAP(0x9a)
+ STRAP(0x9b)
+ STRAP(0x9c)
+ STRAP(0x9d)
+ STRAP(0x9e)
+ STRAP(0x9f)
+ STRAP(0xa0)
+ STRAP(0xa1)
+ STRAP(0xa2)
+ STRAP(0xa3)
+ STRAP(0xa4)
+ STRAP(0xa5)
+ STRAP(0xa6)
+ STRAP(0xa7)
+ STRAP(0xa8)
+ STRAP(0xa9)
+ STRAP(0xaa)
+ STRAP(0xab)
+ STRAP(0xac)
+ STRAP(0xad)
+ STRAP(0xae)
+ STRAP(0xaf)
+ STRAP(0xb0)
+ STRAP(0xb1)
+ STRAP(0xb2)
+ STRAP(0xb3)
+ STRAP(0xb4)
+ STRAP(0xb5)
+ STRAP(0xb6)
+ STRAP(0xb7)
+ STRAP(0xb8)
+ STRAP(0xb9)
+ STRAP(0xba)
+ STRAP(0xbb)
+ STRAP(0xbc)
+ STRAP(0xbd)
+ STRAP(0xbe)
+ STRAP(0xbf)
+ STRAP(0xc0)
+ STRAP(0xc1)
+ STRAP(0xc2)
+ STRAP(0xc3)
+ STRAP(0xc4)
+ STRAP(0xc5)
+ STRAP(0xc6)
+ STRAP(0xc7)
+ STRAP(0xc8)
+ STRAP(0xc9)
+ STRAP(0xca)
+ STRAP(0xcb)
+ STRAP(0xcc)
+ STRAP(0xcd)
+ STRAP(0xce)
+ STRAP(0xcf)
+ STRAP(0xd0)
+ STRAP(0xd1)
+ STRAP(0xd2)
+ STRAP(0xd3)
+ STRAP(0xd4)
+ STRAP(0xd5)
+ STRAP(0xd6)
+ STRAP(0xd7)
+ STRAP(0xd8)
+ STRAP(0xd9)
+ STRAP(0xda)
+ STRAP(0xdb)
+ STRAP(0xdc)
+ STRAP(0xdd)
+ STRAP(0xde)
+ STRAP(0xdf)
+ STRAP(0xe0)
+ STRAP(0xe1)
+ STRAP(0xe2)
+ STRAP(0xe3)
+ STRAP(0xe4)
+ STRAP(0xe5)
+ STRAP(0xe6)
+ STRAP(0xe7)
+ STRAP(0xe8)
+ STRAP(0xe9)
+ STRAP(0xea)
+ STRAP(0xeb)
+ STRAP(0xec)
+ STRAP(0xed)
+ STRAP(0xee)
+ STRAP(0xef)
+ STRAP(0xf0)
+ STRAP(0xf1)
+ STRAP(0xf2)
+ STRAP(0xf3)
+ STRAP(0xf4)
+ STRAP(0xf5)
+ STRAP(0xf6)
+ STRAP(0xf7)
+ STRAP(0xf8)
+ STRAP(0xf9)
+ STRAP(0xfa)
+ STRAP(0xfb)
+ STRAP(0xfc)
+ STRAP(0xfd)
+ STRAP(0xfe)
+ STRAP(0xff)
+
+/*
+ * Pad the trap table to max page size.
+ * Trap table size is 0x100 * 4instr * 4byte/instr = 4096 bytes only.
+ */
+ .align PAGE_SIZE
+
+#ifdef DEBUG
+/*
+ * A hardware red zone is impossible. We simulate one in software by
+ * keeping a `red zone' pointer; if %sp becomes less than this, we panic.
+ * This is expensive and is only enabled when debugging.
+ */
+#define REDSIZE (8*96) /* some room for bouncing */
+#define REDSTACK 2048 /* size of `panic: stack overflow' region */
+ .data
+_C_LABEL(redzone):
+ .word _C_LABEL(idle_u) + REDSIZE
+_C_LABEL(redstack):
+ .skip REDSTACK
+ .text
+Lpanic_red:
+ .asciz "stack overflow"
+ _ALIGN
+
+ /* set stack pointer redzone to base+minstack; alters base */
+#define SET_SP_REDZONE(base, tmp) \
+ add base, REDSIZE, base; \
+ sethi %hi(_C_LABEL(redzone)), tmp; \
+ st base, [tmp + %lo(_C_LABEL(redzone))]
+
+ /* variant with a constant */
+#define SET_SP_REDZONE_CONST(const, tmp1, tmp2) \
+ set (const) + REDSIZE, tmp1; \
+ sethi %hi(_C_LABEL(redzone)), tmp2; \
+ st tmp1, [tmp2 + %lo(_C_LABEL(redzone))]
+
+ /* check stack pointer against redzone (uses two temps) */
+#define CHECK_SP_REDZONE(t1, t2) \
+ sethi %hi(_C_LABEL(redzone)), t1; \
+ ld [t1 + %lo(_C_LABEL(redzone))], t2; \
+ cmp %sp, t2; /* if sp >= t2, not in red zone */ \
+ bgeu 7f; nop; /* and can continue normally */ \
+ /* move to panic stack */ \
+ st %g0, [t1 + %lo(_C_LABEL(redzone))]; \
+ set _C_LABEL(redstack) + REDSTACK - 96, %sp; \
+ /* prevent panic() from lowering ipl */ \
+ sethi %hi(_C_LABEL(panicstr)), t2; \
+ set Lpanic_red, t2; \
+ st t2, [t1 + %lo(_C_LABEL(panicstr))]; \
+ rd %psr, t1; /* t1 = splhigh() */ \
+ or t1, PSR_PIL, t2; \
+ wr t2, 0, %psr; \
+ nop; nop; nop; \
+ wr t2, PSR_ET, %psr; /* turn on traps */ \
+ nop; nop; nop; \
+ save %sp, -CCFSZ, %sp; /* preserve current window */ \
+ sethi %hi(Lpanic_red), %o0; \
+ call _C_LABEL(panic); or %o0, %lo(Lpanic_red), %o0; \
+7:
+
+#else
+
+#define SET_SP_REDZONE(base, tmp)
+#define SET_SP_REDZONE_CONST(const, t1, t2)
+#define CHECK_SP_REDZONE(t1, t2)
+#endif
+
+/*
+ * The window code must verify user stack addresses before using them.
+ * A user stack pointer is invalid if:
+ * - it is not on an 8 byte boundary;
+ * - its pages (a register window, being 64 bytes, can occupy
+ * two pages) are not readable or writable.
+ * We define three separate macros here for testing user stack addresses.
+ *
+ * PTE_OF_ADDR locates a PTE, branching to a `bad address'
+ * handler if the stack pointer does not have a valid mapping;
+ * CMP_PTE_USER_READ compares the located PTE against `user read' mode;
+ * CMP_PTE_USER_WRITE compares the located PTE against `user write' mode.
+ * The compares give `equal' if read or write is OK.
+ *
+ * Note that the user stack pointer usually points into high addresses
+ * (top 3 bits all 1), so that is what we check first.
+ *
+ * The code below also assumes that PTE_OF_ADDR is safe in a delay
+ * slot; it is, at it merely sets its `pte' register to a temporary value.
+ */
+
+ /* input: addr, output: pte; aux: bad address label */
+#define PTE_OF_ADDR(addr, pte, tmp, bad) \
+ srl addr, PDT_INDEX_SHIFT, pte; /* get segment */ \
+ lda [%g0] ASI_PDBR, tmp; \
+ nop; nop; /* XXX */ \
+ sll pte, 3, pte; \
+ add tmp, pte, tmp; /* get PT pointer */ \
+ lda [tmp] ASI_PHYS_CACHED, pte; \
+ tst pte; \
+ be bad; /* bail if no PT */ \
+ srl addr, (PT_INDEX_SHIFT - 2), tmp; \
+ and tmp, ((1 << PT_INDEX_SIZE) -1) << 2, tmp; \
+ add pte, tmp, pte; /* get PTE */ \
+ lda [pte] ASI_PHYS_CACHED, pte; \
+ nop; nop /* XXX */
+
+ /* input: pte; output: condition codes */
+#define CMP_PTE_USER_READ(pte, tmp, label) \
+ and pte, PG_V | PG_S, pte; \
+ cmp pte, PG_V
+
+ /* input: pte; output: condition codes */
+#define CMP_PTE_USER_WRITE(pte, tmp, label) \
+ and pte, PG_V | PG_S | PG_RO, pte; \
+ cmp pte, PG_V
+
+#define INSERT_PTE(addr, pte) \
+ sta addr, [%g0] ASI_FVAR; \
+ sta pte, [%g0] ASI_GTLB_RANDOM
+
+/*
+ * The calculations in PTE_OF_ADDR and CMP_PTE_USER_* are rather slow:
+ * in particular, according to Gordon Irlam of the University of Adelaide
+ * in Australia, these consume at least 18 cycles on an SS1 and 37 on an
+ * SS2. Hence, we try to avoid them in the common case.
+ *
+ * A chunk of 64 bytes is on a single page if and only if:
+ *
+ * ((base + 64 - 1) & ~(NBPG-1)) == (base & ~(NBPG-1))
+ *
+ * Equivalently (and faster to test), the low order bits (base & 4095) must
+ * be small enough so that the sum (base + 63) does not carry out into the
+ * upper page-address bits, i.e.,
+ *
+ * (base & (NBPG-1)) < (NBPG - 63)
+ *
+ * so we allow testing that here. This macro is also assumed to be safe
+ * in a delay slot (modulo overwriting its temporary).
+ */
+#define SLT_IF_1PAGE_RW(addr, tmp, tmp1) \
+ sethi %hi(~PAGE_MASK), tmp1; \
+ andn addr, tmp1, tmp; \
+ set PAGE_MASK - 62, tmp1; \
+ cmp tmp, tmp1
+
+/*
+ * Every trap that enables traps must set up stack space.
+ * If the trap is from user mode, this involves switching to the kernel
+ * stack for the current process, and we must also set cpcb->pcb_uw
+ * so that the window overflow handler can tell user windows from kernel
+ * windows.
+ *
+ * The number of user windows is:
+ *
+ * cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows
+ *
+ * (where pcb_wim = log2(current %wim) and CWP = low 5 bits of %psr).
+ * We compute this expression by table lookup in uwtab[CWP - pcb_wim],
+ * which has been set up as:
+ *
+ * for i in [-nwin+1 .. nwin-1]
+ * uwtab[i] = (nwin - 1 - i) % nwin;
+ *
+ * (If you do not believe this works, try it for yourself.)
+ *
+ * We also keep one or two more tables:
+ *
+ * for i in 0..nwin-1
+ * wmask[i] = 1 << ((i + 1) % nwindows);
+ *
+ * wmask[CWP] tells whether a `rett' would return into the invalid window.
+ */
+ .data
+ .skip 32 ! alignment byte & negative indicies
+uwtab: .skip 32 ! u_char uwtab[-31..31];
+wmask: .skip 32 ! u_char wmask[0..31];
+
+ .text
+/*
+ * Things begin to grow uglier....
+ *
+ * Each trap handler may (always) be running in the trap window.
+ * If this is the case, it cannot enable further traps until it writes
+ * the register windows into the stack (or, if the stack is no good,
+ * the current pcb).
+ *
+ * ASSUMPTIONS: TRAP_SETUP() is called with:
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l3 = (some value that must not be altered)
+ * which means we have 4 registers to work with.
+ *
+ * The `stackspace' argument is the number of stack bytes to allocate
+ * for register-saving, and must be at least -64 (and typically more,
+ * for global registers and %y).
+ *
+ * Trapframes should use -CCFSZ-80. (80 = sizeof(struct trapframe);
+ * see trap.h. This basically means EVERYONE. Interrupt frames could
+ * get away with less, but currently do not.)
+ *
+ * The basic outline here is:
+ *
+ * if (trap came from kernel mode) {
+ * if (we are in the trap window)
+ * save it away;
+ * %sp = %fp - stackspace;
+ * } else {
+ * compute the number of user windows;
+ * if (we are in the trap window)
+ * save it away;
+ * %sp = (top of kernel stack) - stackspace;
+ * }
+ *
+ * Again, the number of user windows is:
+ *
+ * cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows
+ *
+ * (where pcb_wim = log2(current %wim) and CWP is the low 5 bits of %psr),
+ * and this is computed as `uwtab[CWP - pcb_wim]'.
+ *
+ * NOTE: if you change this code, you will have to look carefully
+ * at the window overflow and underflow handlers and make sure they
+ * have similar changes made as needed.
+ */
+#define CALL_CLEAN_TRAP_WINDOW \
+ sethi %hi(clean_trap_window), %l7; \
+ jmpl %l7 + %lo(clean_trap_window), %l4; \
+ mov %g7, %l7 /* save %g7 in %l7 for clean_trap_window */
+
+#define TRAP_SETUP(stackspace) \
+ rd %wim, %l4; \
+ mov 1, %l5; \
+ sll %l5, %l0, %l5; \
+ btst PSR_PS, %l0; \
+ bz 1f; \
+ btst %l5, %l4; \
+ /* came from kernel mode; cond codes indicate trap window */ \
+ bz,a 3f; \
+ add %fp, stackspace, %sp; /* want to just set %sp */ \
+ CALL_CLEAN_TRAP_WINDOW; /* but maybe need to clean first */ \
+ b 3f; \
+ add %fp, stackspace, %sp; \
+1: \
+ /* came from user mode: compute pcb_nw */ \
+ sethi %hi(_C_LABEL(cpcb)), %l6; \
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6; \
+ ld [%l6 + PCB_WIM], %l5; \
+ and %l0, 31, %l4; \
+ sub %l4, %l5, %l5; \
+ set uwtab, %l4; \
+ ldub [%l4 + %l5], %l5; \
+ st %l5, [%l6 + PCB_UW]; \
+ /* cond codes still indicate whether in trap window */ \
+ bz,a 2f; \
+ sethi %hi(USPACE+(stackspace)), %l5; \
+ /* yes, in trap window; must clean it */ \
+ CALL_CLEAN_TRAP_WINDOW; \
+ sethi %hi(_C_LABEL(cpcb)), %l6; \
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6; \
+ sethi %hi(USPACE+(stackspace)), %l5; \
+2: \
+ /* trap window is (now) clean: set %sp */ \
+ or %l5, %lo(USPACE+(stackspace)), %l5; \
+ add %l6, %l5, %sp; \
+ SET_SP_REDZONE(%l6, %l5); \
+3: \
+ CHECK_SP_REDZONE(%l6, %l5)
+
+/*
+ * Interrupt setup is almost exactly like trap setup, but we need to
+ * go to the interrupt stack if (a) we came from user mode or (b) we
+ * came from kernel mode on the kernel stack.
+ */
+#define INTR_SETUP(stackspace) \
+ rd %wim, %l4; \
+ mov 1, %l5; \
+ sll %l5, %l0, %l5; \
+ btst PSR_PS, %l0; \
+ bz 1f; \
+ btst %l5, %l4; \
+ /* came from kernel mode; cond codes still indicate trap window */ \
+ bz,a 0f; \
+ sethi %hi(_C_LABEL(eintstack)), %l7; \
+ CALL_CLEAN_TRAP_WINDOW; \
+ sethi %hi(_C_LABEL(eintstack)), %l7; \
+0: /* now if %fp >= eintstack, we were on the kernel stack */ \
+ cmp %fp, %l7; \
+ bge,a 3f; \
+ add %l7, stackspace, %sp; /* so switch to intstack */ \
+ b 4f; \
+ add %fp, stackspace, %sp; /* else stay on intstack */ \
+1: \
+ /* came from user mode: compute pcb_nw */ \
+ sethi %hi(_C_LABEL(cpcb)), %l6; \
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6; \
+ ld [%l6 + PCB_WIM], %l5; \
+ and %l0, 31, %l4; \
+ sub %l4, %l5, %l5; \
+ set uwtab, %l4; \
+ ldub [%l4 + %l5], %l5; \
+ st %l5, [%l6 + PCB_UW]; \
+ /* cond codes still indicate whether in trap window */ \
+ bz,a 2f; \
+ sethi %hi(_C_LABEL(eintstack)), %l7; \
+ /* yes, in trap window; must save regs */ \
+ CALL_CLEAN_TRAP_WINDOW; \
+ sethi %hi(_C_LABEL(eintstack)), %l7; \
+2: \
+ add %l7, stackspace, %sp; \
+3: \
+ SET_SP_REDZONE_CONST(_C_LABEL(intstack), %l6, %l5); \
+4: \
+ CHECK_SP_REDZONE(%l6, %l5)
+
+/*
+ * Handler for making the trap window shiny clean.
+ *
+ * On entry:
+ * cpcb->pcb_nw = number of user windows
+ * %l0 = %psr
+ * %l1 must not be clobbered
+ * %l2 must not be clobbered
+ * %l3 must not be clobbered
+ * %l4 = address for `return'
+ * %l7 = saved %g7 (we put this in a delay slot above, to save work)
+ *
+ * On return:
+ * %wim has changed, along with cpcb->pcb_wim
+ * %g7 has been restored
+ *
+ * Normally, we push only one window.
+ */
+clean_trap_window:
+ mov %g5, %l5 ! save %g5
+ mov %g6, %l6 ! ... and %g6
+/* mov %g7, %l7 ! ... and %g7 (already done for us) */
+ sethi %hi(_C_LABEL(cpcb)), %g6 ! get current pcb
+ ld [%g6 + %lo(_C_LABEL(cpcb))], %g6
+
+ /* Figure out whether it is a user window (cpcb->pcb_uw > 0). */
+ ld [%g6 + PCB_UW], %g7
+ deccc %g7
+ bge ctw_user
+ save %g0, %g0, %g0 ! in any case, enter window to save
+
+ /* The window to be pushed is a kernel window. */
+ std %i6, [%sp + (7*8)]
+ std %l0, [%sp + (0*8)]
+
+ctw_merge:
+ !! std %l0, [%sp + (0*8)] ! Done by delay slot or above
+ std %l2, [%sp + (1*8)]
+ std %l4, [%sp + (2*8)]
+ std %l6, [%sp + (3*8)]
+ std %i0, [%sp + (4*8)]
+ std %i2, [%sp + (5*8)]
+ std %i4, [%sp + (6*8)]
+ !! std %i6, [%sp + (7*8)] ! Done above or by StackGhost
+
+ /* Set up new window invalid mask, and update cpcb->pcb_wim. */
+ rd %psr, %g7 ! g7 = (junk << 5) + new_cwp
+ mov 1, %g5 ! g5 = 1 << new_cwp;
+ sll %g5, %g7, %g5
+ wr %g5, 0, %wim ! setwim(g5);
+ and %g7, 31, %g7 ! cpcb->pcb_wim = g7 & 31;
+ sethi %hi(_C_LABEL(cpcb)), %g6 ! re-get current pcb
+ ld [%g6 + %lo(_C_LABEL(cpcb))], %g6
+ st %g7, [%g6 + PCB_WIM]
+ nop
+ restore ! back to trap window
+
+ mov %l5, %g5 ! restore g5
+ mov %l6, %g6 ! ... and g6
+ jmp %l4 + 8 ! return to caller
+ mov %l7, %g7 ! ... and g7
+ /* NOTREACHED */
+
+
+ctw_stackghost:
+ !! StackGhost Encrypt
+ sethi %hi(_C_LABEL(cpcb)), %g6 ! get current *pcb
+ ld [%g6 + %lo(_C_LABEL(cpcb))], %g6 ! dereference *pcb
+ ld [%g6 + PCB_WCOOKIE], %l0 ! get window cookie
+ xor %l0, %i7, %i7 ! mix in cookie
+ b ctw_merge
+ std %i6, [%sp + (7*8)]
+
+ctw_user:
+ /*
+ * The window to be pushed is a user window.
+ * We must verify the stack pointer (alignment & permissions).
+ * See comments above definition of PTE_OF_ADDR.
+ */
+ st %g7, [%g6 + PCB_UW] ! cpcb->pcb_uw--;
+ btst 7, %sp ! if not aligned,
+ bne ctw_invalid ! choke on it
+ EMPTY
+
+ PTE_OF_ADDR(%sp, %g7, %g6, ctw_invalid)
+ CMP_PTE_USER_WRITE(%g7, %g5, NOP_ON_4M_2) ! likewise if not writable
+ bne ctw_invalid
+ nop
+ INSERT_PTE(%sp, %g7)
+ SLT_IF_1PAGE_RW(%sp, %g7, %g6)
+ bl,a ctw_stackghost ! all ok if only 1
+ std %l0, [%sp]
+ add %sp, 7 * 8, %g5 ! check last addr too
+ PTE_OF_ADDR(%g5, %g7, %g6, ctw_invalid)
+ CMP_PTE_USER_WRITE(%g7, %g6, NOP_ON_4M_4)
+ INSERT_PTE(%g5, %g7)
+ be,a ctw_stackghost ! all ok: store <l0,l1> and merge
+ std %l0, [%sp]
+
+ /*
+ * The window we wanted to push could not be pushed.
+ * Instead, save ALL user windows into the pcb.
+ * We will notice later that we did this, when we
+ * get ready to return from our trap or syscall.
+ *
+ * The code here is run rarely and need not be optimal.
+ */
+ctw_invalid:
+ /*
+ * Reread cpcb->pcb_uw. We decremented this earlier,
+ * so it is off by one.
+ */
+ sethi %hi(_C_LABEL(cpcb)), %g6 ! re-get current pcb
+ ld [%g6 + %lo(_C_LABEL(cpcb))], %g6
+
+ ld [%g6 + PCB_UW], %g7 ! (number of user windows) - 1
+ add %g6, PCB_RW, %g5
+
+ /* save g7+1 windows, starting with the current one */
+1: ! do {
+ std %l0, [%g5 + (0*8)] ! rw->rw_local[0] = l0;
+ std %l2, [%g5 + (1*8)] ! ...
+ std %l4, [%g5 + (2*8)]
+ std %l6, [%g5 + (3*8)]
+ std %i0, [%g5 + (4*8)]
+ std %i2, [%g5 + (5*8)]
+ std %i4, [%g5 + (6*8)]
+
+ !! StackGhost Encrypt (PCP)
+ ! pcb already dereferenced in %g6
+ ld [%g6 + PCB_WCOOKIE], %l0 ! get window cookie
+ xor %l0, %i7, %i7 ! mix in cookie
+ std %i6, [%g5 + (7*8)]
+
+ deccc %g7 ! if (n > 0) save(), rw++;
+ bge,a 1b ! } while (--n >= 0);
+ save %g5, 64, %g5
+
+ /* stash sp for bottommost window */
+ st %sp, [%g5 + 64 + (7*8)]
+
+ /* set up new wim */
+ rd %psr, %g7 ! g7 = (junk << 5) + new_cwp;
+ mov 1, %g5 ! g5 = 1 << new_cwp;
+ sll %g5, %g7, %g5
+ wr %g5, 0, %wim ! wim = g5;
+ and %g7, 31, %g7
+ st %g7, [%g6 + PCB_WIM] ! cpcb->pcb_wim = new_cwp;
+
+ /* fix up pcb fields */
+ ld [%g6 + PCB_UW], %g7 ! n = cpcb->pcb_uw;
+ add %g7, 1, %g5
+ st %g5, [%g6 + PCB_NSAVED] ! cpcb->pcb_nsaved = n + 1;
+ st %g0, [%g6 + PCB_UW] ! cpcb->pcb_uw = 0;
+
+ /* return to trap window */
+1: deccc %g7 ! do {
+ bge 1b ! restore();
+ restore ! } while (--n >= 0);
+
+ mov %l5, %g5 ! restore g5, g6, & g7, and return
+ mov %l6, %g6
+ jmp %l4 + 8
+ mov %l7, %g7
+ /* NOTREACHED */
+
+/*
+ * TLB miss handlers
+ *
+ * For now, they are the same - we simply extract the faulting PTE, and
+ * insert it at a random place into the TLB cache.
+ */
+
+dtlb_miss:
+itlb_miss:
+ lda [%g0] ASI_PDER, %l3
+ lda [%g0] ASI_PTOR, %l4
+ lda [%g0] ASI_PDBR, %l5
+ lda [%l3] ASI_PHYS_CACHED, %l3 ! get PDE
+ add %l3, %l4, %l3
+ lda [%l3] ASI_PHYS_CACHED, %l4 ! get PTE
+#if 0
+ or %l4, PG_U, %l4 ! set PG_U
+ sta %l4, [%l3] ASI_PHYS_CACHED ! and write modified PTE back
+#endif
+ sta %l4, [%g0] ASI_GTLB_RANDOM ! insert into tlb
+ mov %l0, %psr ! restore PSR_CC
+ nop
+ RETT
+
+/*
+ * Double trap
+ *
+ * This usually happens during a TLB miss with traps disabled.
+ * We save a few registers to help debugging, and reset the machine.
+ */
+
+double_trap:
+ set PROM_DATA_PA + SB_REGS, %l3
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %l1, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %l2, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_FCR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_FVAR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_FPAR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_FPSR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_PDER, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_PTOR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_PDBR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ lda [%g0] ASI_MMCR, %l0
+ sta %l0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g1, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g2, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g3, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g4, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g5, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g6, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %g7, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o0, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o1, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o2, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o3, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o4, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o5, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o6, [%l3] ASI_PHYS_BW
+ add %l3, 4, %l3
+ sta %o7, [%l3] ASI_PHYS_BW
+
+ wr %g0, %wim
+ nop; nop; nop
+ restore
+
+ set GLU_RESET, %o0
+ sta %g0, [%o0] ASI_PHYS_IO
+1: b 1b
+ nop
+
+/*
+ * Each memory access (text or data) fault, from user or kernel mode,
+ * comes here. We read the error register and figure out what has
+ * happened.
+ *
+ * This cannot be done from C code since we must not enable traps (and
+ * hence may not use the `save' instruction) until we have decided that
+ * the error is or is not an asynchronous one that showed up after a
+ * synchronous error, but which must be handled before the sync err.
+ *
+ * Most memory faults are user mode text or data faults, which can cause
+ * signal delivery or ptracing, for which we must build a full trapframe.
+ * It does not seem worthwhile to work to avoid this in the other cases,
+ * so we store all the %g registers on the stack immediately.
+ *
+ * On entry:
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l3 = T_TEXTFAULT or T_DATAFAULT
+ *
+ * Internal:
+ * %l4 = %y, until we call mem_access_fault (then onto trapframe)
+ * %l5 = IE_reg_addr, if async mem error
+ *
+ */
+
+mem_fault:
+ /*
+ * This ends up close to slowtrap (except for ECC errors),
+ * but we don't invoke trap() in the end.
+ */
+
+ TRAP_SETUP(-CCFSZ-80)
+
+ st %g1, [%sp + CCFSZ + 20] ! save g1
+ rd %y, %l4 ! save y
+
+ std %g2, [%sp + CCFSZ + 24] ! save g2, g3
+ lda [%g0] ASI_FCR, %o1 ! fault cause reg
+ std %g4, [%sp + CCFSZ + 32] ! (sneak g4,g5 in here)
+ lda [%g0] ASI_FVAR, %o2 ! virt addr
+ btst FCR_ECC_SINGLE | FCR_ECC_MULTIPLE, %o1 ! ECC memory error?
+ std %g6, [%sp + CCFSZ + 40]
+ CLEAR_FCR
+ bz normal_mem_fault ! no, just a regular fault
+ wr %l0, PSR_ET, %psr ! (and reenable traps)
+
+ /*
+ * We got an ECC memory error.
+ */
+ clr %o3
+ clr %o4
+ call _C_LABEL(ecc_fault) ! ecc_fault(0, fcr, va, 0, 0)
+ clr %o0
+
+ b mem_fault_return
+ wr %l4, 0, %y
+
+normal_mem_fault:
+ /*
+ * call C code to deal with the trap.
+ * Must finish trap frame (psr,pc,npc,%y,%o0..%o7) in case
+ * we decide to deliver a signal or ptrace the process.
+ * %g1..%g7 were already set up above.
+ */
+ std %l0, [%sp + CCFSZ + 0] ! set tf.tf_psr, tf.tf_pc
+ mov %l3, %o0 ! (argument: type)
+ st %l2, [%sp + CCFSZ + 8] ! set tf.tf_npc
+ st %l4, [%sp + CCFSZ + 12] ! set tf.tf_y
+ mov %l1, %o3 ! (argument: pc)
+ std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc
+ std %i2, [%sp + CCFSZ + 56]
+ mov %l0, %o4 ! (argument: psr)
+ std %i4, [%sp + CCFSZ + 64]
+ std %i6, [%sp + CCFSZ + 72]
+ call _C_LABEL(mem_access_fault) ! mem_access_fault(type, fcr,
+ ! va, pc, psr, &tf);
+ add %sp, CCFSZ, %o5 ! (argument: &tf)
+
+ ldd [%sp + CCFSZ + 0], %l0 ! load new values
+ ldd [%sp + CCFSZ + 8], %l2
+ wr %l4, 0, %y
+ ldd [%sp + CCFSZ + 48], %i0
+ ldd [%sp + CCFSZ + 56], %i2
+ ldd [%sp + CCFSZ + 64], %i4
+ ldd [%sp + CCFSZ + 72], %i6
+mem_fault_return:
+ ld [%sp + CCFSZ + 20], %g1
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ ldd [%sp + CCFSZ + 40], %g6
+
+ b return_from_trap ! go return
+ wr %l0, 0, %psr ! (but first disable traps again)
+
+
+/*
+ * fp_exception has to check to see if we are trying to save
+ * the FP state, and if so, continue to save the FP state.
+ *
+ * We do not even bother checking to see if we were in kernel mode,
+ * since users have no access to the special_fp_store instruction.
+ *
+ * This whole idea was stolen from Sprite.
+ */
+fp_exception:
+ set special_fp_store, %l4 ! see if we came from the special one
+ cmp %l1, %l4 ! pc == special_fp_store?
+ bne slowtrap ! no, go handle per usual
+ EMPTY
+ sethi %hi(savefpcont), %l4 ! yes, "return" to the special code
+ or %lo(savefpcont), %l4, %l4
+ jmp %l4
+ rett %l4 + 4
+
+/*
+ * slowtrap() builds a trap frame and calls trap().
+ * This is called `slowtrap' because it *is*....
+ * We have to build a full frame for ptrace(), for instance.
+ *
+ * Registers:
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l3 = trap code
+ */
+slowtrap:
+ TRAP_SETUP(-CCFSZ-80)
+ /*
+ * Phew, ready to enable traps and call C code.
+ */
+ mov %l3, %o0 ! put type in %o0 for later
+Lslowtrap_reenter:
+ wr %l0, PSR_ET, %psr ! traps on again
+ std %l0, [%sp + CCFSZ] ! tf.tf_psr = psr; tf.tf_pc = ret_pc;
+ rd %y, %l3
+ std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc = return_npc; tf.tf_y = %y;
+ st %g1, [%sp + CCFSZ + 20]
+ std %g2, [%sp + CCFSZ + 24]
+ std %g4, [%sp + CCFSZ + 32]
+ std %g6, [%sp + CCFSZ + 40]
+ std %i0, [%sp + CCFSZ + 48]
+ mov %l0, %o1 ! (psr)
+ std %i2, [%sp + CCFSZ + 56]
+ mov %l1, %o2 ! (pc)
+ std %i4, [%sp + CCFSZ + 64]
+ add %sp, CCFSZ, %o3 ! (&tf)
+ call _C_LABEL(trap) ! trap(type, psr, pc, &tf)
+ std %i6, [%sp + CCFSZ + 72]
+
+ ldd [%sp + CCFSZ], %l0 ! load new values
+ ldd [%sp + CCFSZ + 8], %l2
+ wr %l3, 0, %y
+ ld [%sp + CCFSZ + 20], %g1
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ ldd [%sp + CCFSZ + 40], %g6
+ ldd [%sp + CCFSZ + 48], %i0
+ ldd [%sp + CCFSZ + 56], %i2
+ ldd [%sp + CCFSZ + 64], %i4
+ ldd [%sp + CCFSZ + 72], %i6
+ b return_from_trap
+ wr %l0, 0, %psr
+
+/*
+ * Do a `software' trap by re-entering the trap code, possibly first
+ * switching from interrupt stack to kernel stack. This is used for
+ * scheduling and signal ASTs (which generally occur from softclock or
+ * tty or net interrupts) and register window saves (which might occur
+ * from anywhere).
+ *
+ * The current window is the trap window, and it is by definition clean.
+ * We enter with the trap type in %o0. All we have to do is jump to
+ * Lslowtrap_reenter above, but maybe after switching stacks....
+ */
+softtrap:
+ sethi %hi(_C_LABEL(eintstack)), %l7
+ cmp %sp, %l7
+ bge Lslowtrap_reenter
+ EMPTY
+ sethi %hi(_C_LABEL(cpcb)), %l6
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6
+ set USPACE-CCFSZ-80, %l5
+ add %l6, %l5, %l7
+ SET_SP_REDZONE(%l6, %l5)
+ b Lslowtrap_reenter
+ mov %l7, %sp
+
+#ifdef KGDB
+/*
+ * bpt is entered on all breakpoint traps.
+ * If this is a kernel breakpoint, we do not want to call trap().
+ * Among other reasons, this way we can set breakpoints in trap().
+ */
+bpt:
+ btst PSR_PS, %l0 ! breakpoint from kernel?
+ bz slowtrap ! no, go do regular trap
+ nop
+
+ /*
+ * Build a trap frame for kgdb_trap_glue to copy.
+ * Enable traps but set ipl high so that we will not
+ * see interrupts from within breakpoints.
+ */
+ TRAP_SETUP(-CCFSZ-80)
+ or %l0, PSR_PIL, %l4 ! splhigh()
+ wr %l4, 0, %psr ! the manual claims that this
+ nop; nop; nop
+ wr %l4, PSR_ET, %psr ! song and dance is necessary
+ std %l0, [%sp + CCFSZ + 0] ! tf.tf_psr, tf.tf_pc
+ mov %l3, %o0 ! trap type arg for kgdb_trap_glue
+ rd %y, %l3
+ std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc, tf.tf_y
+ rd %wim, %l3
+ st %l3, [%sp + CCFSZ + 16] ! tf.tf_wim (a kgdb-only r/o field)
+ st %g1, [%sp + CCFSZ + 20] ! tf.tf_global[1]
+ std %g2, [%sp + CCFSZ + 24] ! etc
+ std %g4, [%sp + CCFSZ + 32]
+ std %g6, [%sp + CCFSZ + 40]
+ std %i0, [%sp + CCFSZ + 48] ! tf.tf_in[0..1]
+ std %i2, [%sp + CCFSZ + 56] ! etc
+ std %i4, [%sp + CCFSZ + 64]
+ std %i6, [%sp + CCFSZ + 72]
+
+ /*
+ * Now call kgdb_trap_glue(); if it returns, call trap().
+ */
+ mov %o0, %l3 ! gotta save trap type
+ call _C_LABEL(kgdb_trap_glue) ! kgdb_trap_glue(type, &trapframe)
+ add %sp, CCFSZ, %o1 ! (&trapframe)
+
+ /*
+ * Use slowtrap to call trap---but first erase our tracks
+ * (put the registers back the way they were).
+ */
+ mov %l3, %o0 ! slowtrap will need trap type
+ ld [%sp + CCFSZ + 12], %l3
+ wr %l3, 0, %y
+ ld [%sp + CCFSZ + 20], %g1
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ b Lslowtrap_reenter
+ ldd [%sp + CCFSZ + 40], %g6
+
+/*
+ * Enter kernel breakpoint. Write all the windows (not including the
+ * current window) into the stack, so that backtrace works. Copy the
+ * supplied trap frame to the kgdb stack and switch stacks.
+ *
+ * kgdb_trap_glue(type, tf0)
+ * int type;
+ * struct trapframe *tf0;
+ */
+ .globl _C_LABEL(kgdb_trap_glue)
+_C_LABEL(kgdb_trap_glue):
+ save %sp, -CCFSZ, %sp
+
+ call _C_LABEL(write_all_windows)
+ mov %sp, %l4 ! %l4 = current %sp
+
+ /* copy trapframe to top of kgdb stack */
+ set _C_LABEL(kgdb_stack) + KGDB_STACK_SIZE - 80, %l0
+ ! %l0 = tfcopy -> end_of_kgdb_stack
+ mov 80, %l1
+1: ldd [%i1], %l2
+ inc 8, %i1
+ deccc 8, %l1
+ std %l2, [%l0]
+ bg 1b
+ inc 8, %l0
+
+#ifdef DEBUG
+ /* save old red zone and then turn it off */
+ sethi %hi(_C_LABEL(redzone)), %l7
+ ld [%l7 + %lo(_C_LABEL(redzone))], %l6
+ st %g0, [%l7 + %lo(_C_LABEL(redzone))]
+#endif
+ /* switch to kgdb stack */
+ add %l0, -CCFSZ-80, %sp
+
+ /* if (kgdb_trap(type, tfcopy)) kgdb_rett(tfcopy); */
+ mov %i0, %o0
+ call _C_LABEL(kgdb_trap)
+ add %l0, -80, %o1
+ tst %o0
+ bnz,a kgdb_rett
+ add %l0, -80, %g1
+
+ /*
+ * kgdb_trap() did not handle the trap at all so the stack is
+ * still intact. A simple `restore' will put everything back,
+ * after we reset the stack pointer.
+ */
+ mov %l4, %sp
+#ifdef DEBUG
+ st %l6, [%l7 + %lo(_C_LABEL(redzone))] ! restore red zone
+#endif
+ ret
+ restore
+
+/*
+ * Return from kgdb trap. This is sort of special.
+ *
+ * We know that kgdb_trap_glue wrote the window above it, so that we will
+ * be able to (and are sure to have to) load it up. We also know that we
+ * came from kernel land and can assume that the %fp (%i6) we load here
+ * is proper. We must also be sure not to lower ipl (it is at splhigh())
+ * until we have traps disabled, due to the SPARC taking traps at the
+ * new ipl before noticing that PSR_ET has been turned off. We are on
+ * the kgdb stack, so this could be disastrous.
+ *
+ * Note that the trapframe argument in %g1 points into the current stack
+ * frame (current window). We abandon this window when we move %g1->tf_psr
+ * into %psr, but we will not have loaded the new %sp yet, so again traps
+ * must be disabled.
+ */
+kgdb_rett:
+ rd %psr, %g4 ! turn off traps
+ wr %g4, PSR_ET, %psr
+ /* use the three-instruction delay to do something useful */
+ ld [%g1], %g2 ! pick up new %psr
+ ld [%g1 + 12], %g3 ! set %y
+ wr %g3, 0, %y
+#ifdef DEBUG
+ st %l6, [%l7 + %lo(_C_LABEL(redzone))] ! and restore red zone
+#endif
+ wr %g0, 0, %wim ! enable window changes
+ nop; nop; nop
+ /* now safe to set the new psr (changes CWP, leaves traps disabled) */
+ wr %g2, 0, %psr ! set rett psr (including cond codes)
+ /* 3 instruction delay before we can use the new window */
+/*1*/ ldd [%g1 + 24], %g2 ! set new %g2, %g3
+/*2*/ ldd [%g1 + 32], %g4 ! set new %g4, %g5
+/*3*/ ldd [%g1 + 40], %g6 ! set new %g6, %g7
+
+ /* now we can use the new window */
+ mov %g1, %l4
+ ld [%l4 + 4], %l1 ! get new pc
+ ld [%l4 + 8], %l2 ! get new npc
+ ld [%l4 + 20], %g1 ! set new %g1
+
+ /* set up returnee's out registers, including its %sp */
+ ldd [%l4 + 48], %i0
+ ldd [%l4 + 56], %i2
+ ldd [%l4 + 64], %i4
+ ldd [%l4 + 72], %i6
+
+ /* load returnee's window, making the window above it be invalid */
+ restore
+ restore %g0, 1, %l1 ! move to inval window and set %l1 = 1
+ rd %psr, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0, %wim ! %wim = 1 << (%psr & 31)
+ sethi %hi(_C_LABEL(cpcb)), %l1
+ ld [%l1 + %lo(_C_LABEL(cpcb))], %l1
+ and %l0, 31, %l0 ! CWP = %psr & 31;
+ st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = CWP;
+ save %g0, %g0, %g0 ! back to window to reload
+ LOADWIN(%sp)
+ save %g0, %g0, %g0 ! back to trap window
+ /* note, we have not altered condition codes; safe to just rett */
+ RETT
+#endif
+
+/*
+ * syscall() builds a trap frame and calls syscall().
+ * sun_syscall is same but delivers sun system call number
+ * XXX should not have to save&reload ALL the registers just for
+ * ptrace...
+ */
+_C_LABEL(_syscall):
+ TRAP_SETUP(-CCFSZ-80)
+ wr %l0, PSR_ET, %psr
+ std %l0, [%sp + CCFSZ + 0] ! tf_psr, tf_pc
+ rd %y, %l3
+ std %l2, [%sp + CCFSZ + 8] ! tf_npc, tf_y
+ st %g1, [%sp + CCFSZ + 20] ! tf_g[1]
+ std %g2, [%sp + CCFSZ + 24] ! tf_g[2], tf_g[3]
+ std %g4, [%sp + CCFSZ + 32] ! etc
+ std %g6, [%sp + CCFSZ + 40]
+ mov %g1, %o0 ! (code)
+ std %i0, [%sp + CCFSZ + 48]
+ add %sp, CCFSZ, %o1 ! (&tf)
+ std %i2, [%sp + CCFSZ + 56]
+ mov %l1, %o2 ! (pc)
+ std %i4, [%sp + CCFSZ + 64]
+ call _C_LABEL(syscall) ! syscall(code, &tf, pc, suncompat)
+ std %i6, [%sp + CCFSZ + 72]
+ ! now load em all up again, sigh
+ ldd [%sp + CCFSZ + 0], %l0 ! new %psr, new pc
+ ldd [%sp + CCFSZ + 8], %l2 ! new npc, new %y
+ wr %l3, 0, %y
+ /* see `proc_trampoline' for the reason for this label */
+return_from_syscall:
+ ld [%sp + CCFSZ + 20], %g1
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ ldd [%sp + CCFSZ + 40], %g6
+ ldd [%sp + CCFSZ + 48], %i0
+ ldd [%sp + CCFSZ + 56], %i2
+ ldd [%sp + CCFSZ + 64], %i4
+ ldd [%sp + CCFSZ + 72], %i6
+ b return_from_trap
+ wr %l0, 0, %psr
+
+/*
+ * Interrupts. Software interrupts must be cleared from the software
+ * interrupt enable register. Rather than calling ienab_bic for each,
+ * we do them in-line before enabling traps.
+ *
+ * After preliminary setup work, the interrupt is passed to each
+ * registered handler in turn. These are expected to return 1 if they
+ * took care of the interrupt, 0 if they didn't, and -1 if the device
+ * isn't sure. If a handler claims the interrupt, we exit
+ * (hardware interrupts are latched in the requestor so we'll
+ * just take another interrupt in the unlikely event of simultaneous
+ * interrupts from two different devices at the same level). If we go
+ * through all the registered handlers and no one claims it, we report a
+ * stray interrupt. This is more or less done as:
+ *
+ * for (ih = intrhand[intlev]; ih; ih = ih->ih_next)
+ * if ((*ih->ih_fun)(ih->ih_arg ? ih->ih_arg : &frame))
+ * return;
+ * strayintr(&frame);
+ *
+ * Software interrupts are almost the same with three exceptions:
+ * (1) we clear the interrupt from the software interrupt enable
+ * register before calling any handler (we have to clear it first
+ * to avoid an interrupt-losing race),
+ * (2) we always call all the registered handlers (there is no way
+ * to tell if the single bit in the software interrupt register
+ * represents one or many requests)
+ * (3) we never announce a stray interrupt (because of (1), another
+ * interrupt request can come in while we're in the handler. If
+ * the handler deals with everything for both the original & the
+ * new request, we'll erroneously report a stray interrupt when
+ * we take the software interrupt for the new request.
+ *
+ * Inputs:
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l3 = interrupt level
+ * (software interrupt only) %l4 = bits to clear in interrupt register
+ *
+ * Internal:
+ * %l4, %l5: local variables
+ * %l6 = %y
+ * %l7 = %g1
+ * %g2..%g7 go to stack
+ *
+ * An interrupt frame is built in the space for a full trapframe;
+ * this contains the psr, pc, npc, and interrupt level.
+ */
+softintr_kap:
+ sethi %hi(_C_LABEL(kap_sir)), %l6
+ mov 1, %l5
+ sll %l5, %l4, %l5 ! l5 = 1 << (intno - 1)
+ ld [%l6 + %lo(_C_LABEL(kap_sir))], %l4
+ andn %l4, %l5, %l4 ! clear bit
+ /* skip loop if kap_sir becomes zero */
+ clr %l5 ! intnum = 0;
+ cmp %l4, 0
+ be 2f
+ st %l4, [%l6 + %lo(_C_LABEL(kap_sir))]
+ /* now find out the topmost bit set in kap_sir */
+1: srl %l4, 1, %l4 ! while (sir >>= 1)
+ cmp %l4, 0
+ bne 1b
+ inc %l5 ! intnum++;
+2: set GLU_ICR, %l6
+ lda [%l6] ASI_PHYS_IO, %l4 ! read icr
+ srl %l4, 24, %l4
+ andn %l4, GICR_DISPATCH_MASK, %l4 ! clear softintr bits
+ or %l4, %l5, %l4 ! put our number
+ sll %l4, 24, %l4
+ sta %l4, [%l6] ASI_PHYS_IO ! write back icr
+
+softintr_common:
+ INTR_SETUP(-CCFSZ-80)
+ std %g2, [%sp + CCFSZ + 24] ! save registers
+ INCR(_C_LABEL(uvmexp)+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
+ mov %g1, %l7
+ rd %y, %l6
+ std %g4, [%sp + CCFSZ + 32]
+ andn %l0, PSR_PIL, %l4 ! %l4 = psr & ~PSR_PIL |
+ sll %l3, 8, %l5 ! intlev << IPLSHIFT
+ std %g6, [%sp + CCFSZ + 40]
+ or %l5, %l4, %l4 ! ;
+ wr %l4, 0, %psr ! the manual claims this
+ nop; nop; nop
+ wr %l4, PSR_ET, %psr ! song and dance is necessary
+ std %l0, [%sp + CCFSZ + 0] ! set up intrframe/clockframe
+ sll %l3, 2, %l5
+ std %l2, [%sp + CCFSZ + 8]
+ set _C_LABEL(intrhand), %l4 ! %l4 = intrhand[intlev];
+ ld [%l4 + %l5], %l4
+ b 3f
+ st %fp, [%sp + CCFSZ + 16]
+
+1: rd %psr, %o1
+ ld [%l4 + IH_IPL], %o0
+ and %o1, ~PSR_PIL, %o1
+ wr %o1, %o0, %psr
+ ld [%l4 + IH_ARG], %o0
+ ld [%l4 + IH_FUN], %o1
+ tst %o0
+ bz,a 2f
+ add %sp, CCFSZ, %o0
+2: jmpl %o1, %o7 ! (void)(*ih->ih_fun)(...)
+ nop
+ mov %l4, %l3
+ ldd [%l3 + IH_COUNT], %l4
+ inccc %l5
+ addx %l4, 0, %l4
+ std %l4, [%l3 + IH_COUNT]
+ ld [%l3 + IH_NEXT], %l4 ! and ih = ih->ih_next
+3: tst %l4 ! while ih != NULL
+ bnz 1b
+ nop
+ mov %l7, %g1
+ wr %l6, 0, %y
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ ldd [%sp + CCFSZ + 40], %g6
+ b return_from_trap
+ wr %l0, 0, %psr
+
+ /*
+ * _sparc_interrupt{44c,4m} is exported for paranoia checking
+ * (see intr.c).
+ */
+ .globl _C_LABEL(sparc_interrupt44c)
+_C_LABEL(sparc_interrupt44c):
+_C_LABEL(sparc_interrupt_common):
+sparc_interruptkap:
+ INTR_SETUP(-CCFSZ-80)
+ std %g2, [%sp + CCFSZ + 24] ! save registers
+ INCR(_C_LABEL(uvmexp)+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
+ mov %g1, %l7
+ rd %y, %l6
+ std %g4, [%sp + CCFSZ + 32]
+ andn %l0, PSR_PIL, %l4 ! %l4 = psr & ~PSR_PIL |
+ sll %l3, 8, %l5 ! intlev << IPLSHIFT
+ std %g6, [%sp + CCFSZ + 40]
+ or %l5, %l4, %l4 ! ;
+ wr %l4, 0, %psr ! the manual claims this
+ nop; nop; nop
+ wr %l4, PSR_ET, %psr ! song and dance is necessary
+ std %l0, [%sp + CCFSZ + 0] ! set up intrframe/clockframe
+ sll %l3, 2, %l5
+ std %l2, [%sp + CCFSZ + 8] ! set up intrframe/clockframe
+ set _C_LABEL(intrhand), %l4 ! %l4 = intrhand[intlev];
+ ld [%l4 + %l5], %l4
+ clr %l5 ! %l5 = 0
+ b 3f
+ st %fp, [%sp + CCFSZ + 16]
+
+1: rd %psr, %o1
+ ld [%l4 + IH_IPL], %o0
+ and %o1, ~PSR_PIL, %o1
+ wr %o1, %o0, %psr
+ ld [%l4 + IH_ARG], %o0
+ ld [%l4 + IH_FUN], %o1
+ tst %o0
+ bz,a 2f
+ add %sp, CCFSZ, %o0
+2: jmpl %o1, %o7 ! handled = (*ih->ih_fun)(...)
+ nop
+ cmp %o0, 1
+ bge 4f ! if (handled >= 1) break
+ or %o0, %l5, %l5 ! and %l5 |= handled
+ ld [%l4 + IH_NEXT], %l4 ! and ih = ih->ih_next
+3: tst %l4
+ bnz 1b ! while (ih)
+ nop
+ tst %l5 ! if (handled) break
+ bnz 5f
+ nop
+ call _C_LABEL(strayintr) ! strayintr(&intrframe)
+ add %sp, CCFSZ, %o0
+5: /* all done: restore registers and go return */
+ mov %l7, %g1
+ wr %l6, 0, %y
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ ldd [%sp + CCFSZ + 40], %g6
+ b return_from_trap
+ wr %l0, 0, %psr
+4:
+ mov %l4, %l3
+ ldd [%l3 + IH_COUNT], %l4
+ inccc %l5
+ addx %l4, 0, %l4
+ b 5b
+ std %l4, [%l3 + IH_COUNT]
+
+/*
+ * Level 15 interrupt. An async memory error has occurred;
+ * take care of it (typically by panicking, but hey...).
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l3 = 15 * 4 (why? just because!)
+ *
+ * Internal:
+ * %l4 = %y
+ * %l5 = %g1
+ * %l6 = %g6
+ * %l7 = %g7
+ * g2, g3, g4, g5 go to stack
+ *
+ * This code is almost the same as that in mem_access_fault,
+ * except that we already know the problem is not a `normal' fault,
+ * and that we must be extra-careful with interrupt enables.
+ */
+
+nmi:
+ INTR_SETUP(-CCFSZ-80)
+ INCR(_C_LABEL(uvmexp)+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
+ /* XXX MIOD We should check for NMI set in the iGLU BSR, and clear
+ * it if set.
+ */
+
+ /*
+ * Level 15 interrupts are nonmaskable, so with traps off,
+ * disable all interrupts to prevent recursion.
+ */
+ wr %l0, PSR_ET, %psr ! okay, turn traps on again
+
+ std %g2, [%sp + CCFSZ + 0] ! save g2, g3
+ rd %y, %l4 ! save y
+
+ std %g4, [%sp + CCFSZ + 8] ! save g4,g5
+ mov %g1, %l5 ! save g1,g6,g7
+ mov %g6, %l6
+ mov %g7, %l7
+
+nmi_common:
+ ! and call C code
+ call _C_LABEL(ecc_fault)
+ clr %o0
+
+ mov %l5, %g1 ! restore g1 through g7
+ ldd [%sp + CCFSZ + 0], %g2
+ ldd [%sp + CCFSZ + 8], %g4
+ wr %l0, 0, %psr ! re-disable traps
+ mov %l6, %g6
+ mov %l7, %g7
+
+ b return_from_trap
+ wr %l4, 0, %y ! restore y
+
+#ifdef GPROF
+ .globl window_of, winof_user
+ .globl window_uf, winuf_user, winuf_ok, winuf_invalid
+ .globl return_from_trap, rft_kernel, rft_user, rft_invalid
+ .globl softtrap, slowtrap
+ .globl clean_trap_window, _C_LABEL(_syscall)
+#endif
+
+/*
+ * Window overflow trap handler.
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ */
+window_of:
+#ifdef TRIVIAL_WINDOW_OVERFLOW_HANDLER
+ /* a trivial version that assumes %sp is ok */
+ /* (for testing only!) */
+ save %g0, %g0, %g0
+ std %l0, [%sp + (0*8)]
+ rd %psr, %l0
+ mov 1, %l1
+ sll %l1, %l0, %l0
+ wr %l0, 0, %wim
+ std %l2, [%sp + (1*8)]
+ std %l4, [%sp + (2*8)]
+ std %l6, [%sp + (3*8)]
+ std %i0, [%sp + (4*8)]
+ std %i2, [%sp + (5*8)]
+ std %i4, [%sp + (6*8)]
+ std %i6, [%sp + (7*8)]
+ restore
+ RETT
+#else
+ /*
+ * This is similar to TRAP_SETUP, but we do not want to spend
+ * a lot of time, so we have separate paths for kernel and user.
+ * We also know for sure that the window has overflowed.
+ */
+ btst PSR_PS, %l0
+ bz winof_user
+ sethi %hi(clean_trap_window), %l7
+
+ /*
+ * Overflow from kernel mode. Call clean_trap_window to
+ * do the dirty work, then just return, since we know prev
+ * window is valid. clean_trap_windows might dump all *user*
+ * windows into the pcb, but we do not care: there is at
+ * least one kernel window (a trap or interrupt frame!)
+ * above us.
+ */
+ jmpl %l7 + %lo(clean_trap_window), %l4
+ mov %g7, %l7 ! for clean_trap_window
+
+ wr %l0, 0, %psr ! put back the @%*! cond. codes
+ nop; nop; nop ! (let them settle in)
+ RETT
+
+winof_user:
+ /*
+ * Overflow from user mode.
+ * If clean_trap_window dumps the registers into the pcb,
+ * rft_user will need to call trap(), so we need space for
+ * a trap frame. We also have to compute pcb_nw.
+ *
+ * SHOULD EXPAND IN LINE TO AVOID BUILDING TRAP FRAME ON
+ * `EASY' SAVES
+ */
+ sethi %hi(_C_LABEL(cpcb)), %l6
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6
+ ld [%l6 + PCB_WIM], %l5
+ and %l0, 31, %l3
+ sub %l3, %l5, %l5 /* l5 = CWP - pcb_wim */
+ set uwtab, %l4
+ ldub [%l4 + %l5], %l5 /* l5 = uwtab[l5] */
+ st %l5, [%l6 + PCB_UW]
+ jmpl %l7 + %lo(clean_trap_window), %l4
+ mov %g7, %l7 ! for clean_trap_window
+ sethi %hi(_C_LABEL(cpcb)), %l6
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6
+ set USPACE-CCFSZ-80, %l5
+ add %l6, %l5, %sp /* over to kernel stack */
+ CHECK_SP_REDZONE(%l6, %l5)
+
+ /*
+ * Copy return_from_trap far enough to allow us
+ * to jump directly to rft_user_or_recover_pcb_windows
+ * (since we know that is where we are headed).
+ */
+! and %l0, 31, %l3 ! still set (clean_trap_window
+ ! leaves this register alone)
+ set wmask, %l6
+ ldub [%l6 + %l3], %l5 ! %l5 = 1 << ((CWP + 1) % nwindows)
+ b rft_user_or_recover_pcb_windows
+ rd %wim, %l4 ! (read %wim first)
+#endif /* end `real' version of window overflow trap handler */
+
+/*
+ * Window underflow trap handler.
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ *
+ * A picture:
+ *
+ * T R I X
+ * 0 0 0 1 0 0 0 (%wim)
+ * [bit numbers increase towards the right;
+ * `restore' moves right & `save' moves left]
+ *
+ * T is the current (Trap) window, R is the window that attempted
+ * a `Restore' instruction, I is the Invalid window, and X is the
+ * window we want to make invalid before we return.
+ *
+ * Since window R is valid, we cannot use rft_user to restore stuff
+ * for us. We have to duplicate its logic. YUCK.
+ *
+ * Incidentally, TRIX are for kids. Silly rabbit!
+ */
+window_uf:
+#ifdef TRIVIAL_WINDOW_UNDERFLOW_HANDLER
+ wr %g0, 0, %wim ! allow us to enter I
+ restore ! to R
+ nop
+ nop
+ restore ! to I
+ restore %g0, 1, %l1 ! to X
+ rd %psr, %l0
+ sll %l1, %l0, %l0
+ wr %l0, 0, %wim
+ save %g0, %g0, %g0 ! back to I
+ LOADWIN(%sp)
+ save %g0, %g0, %g0 ! back to R
+ save %g0, %g0, %g0 ! back to T
+ RETT
+#else
+ wr %g0, 0, %wim ! allow us to enter I
+ btst PSR_PS, %l0
+ restore ! enter window R
+ bz winuf_user
+ restore ! enter window I
+
+ /*
+ * Underflow from kernel mode. Just recover the
+ * registers and go (except that we have to update
+ * the blasted user pcb fields).
+ */
+ restore %g0, 1, %l1 ! enter window X, then set %l1 to 1
+ rd %psr, %l0 ! cwp = %psr & 31;
+ and %l0, 31, %l0
+ sll %l1, %l0, %l1 ! wim = 1 << cwp;
+ wr %l1, 0, %wim ! setwim(wim);
+ sethi %hi(_C_LABEL(cpcb)), %l1
+ ld [%l1 + %lo(_C_LABEL(cpcb))], %l1
+ st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = cwp;
+ save %g0, %g0, %g0 ! back to window I
+ LOADWIN(%sp)
+ save %g0, %g0, %g0 ! back to R
+ save %g0, %g0, %g0 ! and then to T
+ wr %l0, 0, %psr ! fix those cond codes....
+ nop; nop; nop ! (let them settle in)
+ RETT
+
+winuf_user:
+ /*
+ * Underflow from user mode.
+ *
+ * We cannot use rft_user (as noted above) because
+ * we must re-execute the `restore' instruction.
+ * Since it could be, e.g., `restore %l0,0,%l0',
+ * it is not okay to touch R's registers either.
+ *
+ * We are now in window I.
+ */
+ btst 7, %sp ! if unaligned, it is invalid
+ bne winuf_invalid
+ EMPTY
+
+ PTE_OF_ADDR(%sp, %l7, %l4, winuf_invalid)
+ CMP_PTE_USER_READ(%l7, %l5, NOP_ON_4M_6) ! if first page not readable,
+ bne winuf_invalid ! it is invalid
+ nop
+ INSERT_PTE(%sp, %l7)
+ SLT_IF_1PAGE_RW(%sp, %l7, %l4) ! first page is readable
+ bl,a winuf_ok ! if only one page, enter window X
+ restore %g0, 1, %l1 ! and goto ok, & set %l1 to 1
+ add %sp, 7 * 8, %l5
+ PTE_OF_ADDR(%l5, %l7, %l4, winuf_invalid)
+ CMP_PTE_USER_READ(%l7, %l5, NOP_ON_4M_8) ! check second page too
+ INSERT_PTE(%l5, %l7)
+ be,a winuf_ok ! enter window X and goto ok
+ restore %g0, 1, %l1 ! (and then set %l1 to 1)
+
+winuf_invalid:
+ /*
+ * We were unable to restore the window because %sp
+ * is invalid or paged out. Return to the trap window
+ * and call trap(T_WINUF). This will save R to the user
+ * stack, then load both R and I into the pcb rw[] area,
+ * and return with pcb_nsaved set to -1 for success, 0 for
+ * failure. `Failure' indicates that someone goofed with the
+ * trap registers (e.g., signals), so that we need to return
+ * from the trap as from a syscall (probably to a signal handler)
+ * and let it retry the restore instruction later. Note that
+ * window R will have been pushed out to user space, and thus
+ * be the invalid window, by the time we get back here. (We
+ * continue to label it R anyway.) We must also set %wim again,
+ * and set pcb_uw to 1, before enabling traps. (Window R is the
+ * only window, and it is a user window).
+ */
+ save %g0, %g0, %g0 ! back to R
+ save %g0, 1, %l4 ! back to T, then %l4 = 1
+ sethi %hi(_C_LABEL(cpcb)), %l6
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6
+ st %l4, [%l6 + PCB_UW] ! pcb_uw = 1
+ ld [%l6 + PCB_WIM], %l5 ! get log2(%wim)
+ sll %l4, %l5, %l4 ! %l4 = old %wim
+ wr %l4, 0, %wim ! window I is now invalid again
+ set USPACE-CCFSZ-80, %l5
+ add %l6, %l5, %sp ! get onto kernel stack
+ nop
+ CHECK_SP_REDZONE(%l6, %l5)
+
+ /*
+ * Okay, call trap(T_WINUF, psr, pc, &tf).
+ * See `slowtrap' above for operation.
+ */
+ wr %l0, PSR_ET, %psr
+ std %l0, [%sp + CCFSZ + 0] ! tf.tf_psr, tf.tf_pc
+ rd %y, %l3
+ std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc, tf.tf_y
+ mov T_WINUF, %o0
+ st %g1, [%sp + CCFSZ + 20] ! tf.tf_global[1]
+ mov %l0, %o1
+ std %g2, [%sp + CCFSZ + 24] ! etc
+ mov %l1, %o2
+ std %g4, [%sp + CCFSZ + 32]
+ add %sp, CCFSZ, %o3
+ std %g6, [%sp + CCFSZ + 40]
+ std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc
+ std %i2, [%sp + CCFSZ + 56]
+ std %i4, [%sp + CCFSZ + 64]
+ call _C_LABEL(trap) ! trap(T_WINUF, pc, psr, &tf)
+ std %i6, [%sp + CCFSZ + 72] ! tf.tf_out[6]
+
+ ldd [%sp + CCFSZ + 0], %l0 ! new psr, pc
+ ldd [%sp + CCFSZ + 8], %l2 ! new npc, %y
+ wr %l3, 0, %y
+ ld [%sp + CCFSZ + 20], %g1
+ ldd [%sp + CCFSZ + 24], %g2
+ ldd [%sp + CCFSZ + 32], %g4
+ ldd [%sp + CCFSZ + 40], %g6
+ ldd [%sp + CCFSZ + 48], %i0 ! %o0 for window R, etc
+ ldd [%sp + CCFSZ + 56], %i2
+ ldd [%sp + CCFSZ + 64], %i4
+ wr %l0, 0, %psr ! disable traps: test must be atomic
+ ldd [%sp + CCFSZ + 72], %i6
+ sethi %hi(_C_LABEL(cpcb)), %l6
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6
+ ld [%l6 + PCB_NSAVED], %l7 ! if nsaved is -1, we have our regs
+ tst %l7
+ bl,a 1f ! got them
+ wr %g0, 0, %wim ! allow us to enter windows R, I
+ b,a return_from_trap
+
+ /*
+ * Got 'em. Load 'em up.
+ */
+1:
+ mov %g6, %l3 ! save %g6; set %g6 = cpcb
+ mov %l6, %g6
+ st %g0, [%g6 + PCB_NSAVED] ! and clear magic flag
+ restore ! from T to R
+ restore ! from R to I
+ restore %g0, 1, %l1 ! from I to X, then %l1 = 1
+ rd %psr, %l0 ! cwp = %psr;
+ sll %l1, %l0, %l1
+ wr %l1, 0, %wim ! make window X invalid
+ and %l0, 31, %l0
+ st %l0, [%g6 + PCB_WIM] ! cpcb->pcb_wim = cwp;
+ nop ! unnecessary? old wim was 0...
+ save %g0, %g0, %g0 ! back to I
+
+ !!LOADWIN(%g6 + PCB_RW + 64) ! load from rw[1]
+
+ !! StackGhost Decrypt (PCP)
+ ! pcb already dereferenced in %g6
+ ld [%g6 + PCB_WCOOKIE], %l0 ! get window cookie
+ ldd [%g6 + PCB_RW + 64 + 56], %i6
+ xor %l0, %i7, %i7 ! remove cookie
+
+ ldd [%g6 + PCB_RW + 64], %l0 ! load from rw[1]
+ ldd [%g6 + PCB_RW + 64 + 8], %l2
+ ldd [%g6 + PCB_RW + 64 + 16], %l4
+ ldd [%g6 + PCB_RW + 64 + 24], %l6
+ ldd [%g6 + PCB_RW + 64 + 32], %i0
+ ldd [%g6 + PCB_RW + 64 + 40], %i2
+ ldd [%g6 + PCB_RW + 64 + 48], %i4
+
+ save %g0, %g0, %g0 ! back to R
+
+ !! StackGhost Decrypt (PCP)
+ ! pcb already dereferenced in %g6
+ ! (If I was sober, I could potentially re-use the cookie from above)
+ ld [%g6 + PCB_WCOOKIE], %l0 ! get window cookie
+ ldd [%g6 + PCB_RW + 56], %i6
+ xor %l0, %i7, %i7 ! remove cookie
+
+ !!LOADWIN(%g6 + PCB_RW) ! load from rw[0]
+ ldd [%g6 + PCB_RW], %l0 ! load from rw[0]
+ ldd [%g6 + PCB_RW + 8], %l2
+ ldd [%g6 + PCB_RW + 16], %l4
+ ldd [%g6 + PCB_RW + 24], %l6
+ ldd [%g6 + PCB_RW + 32], %i0
+ ldd [%g6 + PCB_RW + 40], %i2
+ ldd [%g6 + PCB_RW + 48], %i4
+
+ save %g0, %g0, %g0 ! back to T
+
+ wr %l0, 0, %psr ! restore condition codes
+ nop; nop
+ mov %l3, %g6 ! fix %g6
+ RETT
+
+ /*
+ * Restoring from user stack, but everything has checked out
+ * as good. We are now in window X, and %l1 = 1. Window R
+ * is still valid and holds user values.
+ */
+winuf_ok:
+ rd %psr, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0, %wim ! make this one invalid
+ sethi %hi(_C_LABEL(cpcb)), %l2
+ ld [%l2 + %lo(_C_LABEL(cpcb))], %l2
+ and %l0, 31, %l0
+ st %l0, [%l2 + PCB_WIM] ! cpcb->pcb_wim = cwp;
+ save %g0, %g0, %g0 ! back to I
+
+ !! StackGhost Decrypt
+ sethi %hi(_C_LABEL(cpcb)), %l0 ! get current *pcb
+ ld [%l0 + %lo(_C_LABEL(cpcb))], %l1 ! dereference *pcb
+ ld [%l1 + PCB_WCOOKIE], %l0 ! get window cookie
+ ldd [%sp + 56], %i6 ! get saved return pointer
+ xor %l0, %i7, %i7 ! remove cookie
+
+ !!LOADWIN(%sp)
+ ldd [%sp], %l0
+ ldd [%sp + 8], %l2
+ ldd [%sp + 16], %l4
+ ldd [%sp + 24], %l6
+ ldd [%sp + 32], %i0
+ ldd [%sp + 40], %i2
+ ldd [%sp + 48], %i4
+
+ save %g0, %g0, %g0 ! back to R
+ save %g0, %g0, %g0 ! back to T
+ wr %l0, 0, %psr ! restore condition codes
+ nop; nop; nop ! it takes three to tangle
+ RETT
+#endif /* end `real' version of window underflow trap handler */
+
+/*
+ * Various return-from-trap routines (see return_from_trap).
+ */
+
+/*
+ * Return from trap, to kernel.
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l4 = %wim
+ * %l5 = bit for previous window
+ */
+rft_kernel:
+ btst %l5, %l4 ! if (wim & l5)
+ bnz 1f ! goto reload;
+ wr %l0, 0, %psr ! but first put !@#*% cond codes back
+
+ /* previous window is valid; just rett */
+ nop; nop; nop ! wait for cond codes to settle in
+ RETT
+
+ /*
+ * Previous window is invalid.
+ * Update %wim and then reload l0..i7 from frame.
+ *
+ * T I X
+ * 0 0 1 0 0 (%wim)
+ * [see picture in window_uf handler]
+ *
+ * T is the current (Trap) window, I is the Invalid window,
+ * and X is the window we want to make invalid. Window X
+ * currently has no useful values.
+ */
+1:
+ wr %g0, 0, %wim ! allow us to enter window I
+ nop; nop; nop ! (it takes a while)
+ restore ! enter window I
+ restore %g0, 1, %l1 ! enter window X, then %l1 = 1
+ rd %psr, %l0 ! CWP = %psr & 31;
+ and %l0, 31, %l0
+ sll %l1, %l0, %l1 ! wim = 1 << CWP;
+ wr %l1, 0, %wim ! setwim(wim);
+ sethi %hi(_C_LABEL(cpcb)), %l1
+ ld [%l1 + %lo(_C_LABEL(cpcb))], %l1
+ st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = l0 & 31;
+ save %g0, %g0, %g0 ! back to window I
+ LOADWIN(%sp)
+ save %g0, %g0, %g0 ! back to window T
+ /*
+ * Note that the condition codes are still set from
+ * the code at rft_kernel; we can simply return.
+ */
+ RETT
+
+/*
+ * Return from trap, to user. Checks for scheduling trap (`ast') first;
+ * will re-enter trap() if set. Note that we may have to switch from
+ * the interrupt stack to the kernel stack in this case.
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ * %l4 = %wim
+ * %l5 = bit for previous window
+ * %l6 = cpcb
+ * If returning to a valid window, just set psr and return.
+ */
+rft_user:
+! sethi %hi(_C_LABEL(want_ast)), %l7 ! (done below)
+ ld [%l7 + %lo(_C_LABEL(want_ast))], %l7
+ tst %l7 ! want AST trap?
+ bne,a softtrap ! yes, re-enter trap with type T_AST
+ mov T_AST, %o0
+
+ btst %l5, %l4 ! if (wim & l5)
+ bnz 1f ! goto reload;
+ wr %l0, 0, %psr ! restore cond codes
+ nop; nop; nop ! (three instruction delay)
+ RETT
+
+ /*
+ * Previous window is invalid.
+ * Before we try to load it, we must verify its stack pointer.
+ * This is much like the underflow handler, but a bit easier
+ * since we can use our own local registers.
+ */
+1:
+ btst 7, %fp ! if unaligned, address is invalid
+ bne rft_invalid
+ EMPTY
+
+ PTE_OF_ADDR(%fp, %l7, %l3, rft_invalid)
+ CMP_PTE_USER_READ(%l7, %l5, NOP_ON_4M_10) ! try first page
+ bne rft_invalid ! no good
+ nop
+ INSERT_PTE(%fp, %l7)
+ SLT_IF_1PAGE_RW(%fp, %l7, %l3)
+ bl,a rft_user_ok ! only 1 page: ok
+ wr %g0, 0, %wim
+ add %fp, 7*8, %l5
+ PTE_OF_ADDR(%l5, %l7, %l3, rft_invalid)
+ INSERT_PTE(%l5, %l7)
+ CMP_PTE_USER_READ(%l7, %l5, NOP_ON_4M_12) ! check 2nd page too
+ be,a rft_user_ok
+ wr %g0, 0, %wim
+
+ /*
+ * The window we wanted to pull could not be pulled. Instead,
+ * re-enter trap with type T_RWRET. This will pull the window
+ * into cpcb->pcb_rw[0] and set cpcb->pcb_nsaved to -1, which we
+ * will detect when we try to return again.
+ */
+rft_invalid:
+ b softtrap
+ mov T_RWRET, %o0
+
+ /*
+ * The window we want to pull can be pulled directly.
+ */
+rft_user_ok:
+! wr %g0, 0, %wim ! allow us to get into it
+ wr %l0, 0, %psr ! fix up the cond codes now
+ nop; nop; nop
+ restore ! enter window I
+ restore %g0, 1, %l1 ! enter window X, then %l1 = 1
+ rd %psr, %l0 ! l0 = (junk << 5) + CWP;
+ sll %l1, %l0, %l1 ! %wim = 1 << CWP;
+ wr %l1, 0, %wim
+ sethi %hi(_C_LABEL(cpcb)), %l1
+ ld [%l1 + %lo(_C_LABEL(cpcb))], %l1
+ and %l0, 31, %l0
+ st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = l0 & 31;
+ save %g0, %g0, %g0 ! back to window I
+
+ !! StackGhost Decrypt
+ sethi %hi(_C_LABEL(cpcb)), %l0 ! get current *pcb
+ ld [%l0 + %lo(_C_LABEL(cpcb))], %l1 ! dereference *pcb
+ ld [%l1 + PCB_WCOOKIE], %l0 ! get window cookie
+ ldd [%sp + 56], %i6 ! get saved return pointer
+ xor %l0, %i7, %i7 ! remove cookie
+
+ !!LOADWIN(%sp) ! suck hard
+ ldd [%sp], %l0
+ ldd [%sp + 8], %l2
+ ldd [%sp + 16], %l4
+ ldd [%sp + 24], %l6
+ ldd [%sp + 32], %i0
+ ldd [%sp + 40], %i2
+ ldd [%sp + 48], %i4
+
+ save %g0, %g0, %g0 ! back to window T
+ RETT
+
+/*
+ * Return from trap. Entered after a
+ * wr %l0, 0, %psr
+ * which disables traps so that we can rett; registers are:
+ *
+ * %l0 = %psr
+ * %l1 = return pc
+ * %l2 = return npc
+ *
+ * (%l3..%l7 anything).
+ *
+ * If we are returning to user code, we must:
+ * 1. Check for register windows in the pcb that belong on the stack.
+ * If there are any, reenter trap with type T_WINOF.
+ * 2. Make sure the register windows will not underflow. This is
+ * much easier in kernel mode....
+ */
+return_from_trap:
+! wr %l0, 0, %psr ! disable traps so we can rett
+! (someone else did this already)
+ and %l0, 31, %l5
+ set wmask, %l6
+ ldub [%l6 + %l5], %l5 ! %l5 = 1 << ((CWP + 1) % nwindows)
+ btst PSR_PS, %l0 ! returning to userland?
+ bnz rft_kernel ! no, go return to kernel
+ rd %wim, %l4 ! (read %wim in any case)
+
+rft_user_or_recover_pcb_windows:
+ /*
+ * (entered with %l4=%wim, %l5=wmask[cwp]; %l0..%l2 as usual)
+ *
+ * check cpcb->pcb_nsaved:
+ * if 0, do a `normal' return to user (see rft_user);
+ * if > 0, cpcb->pcb_rw[] holds registers to be copied to stack;
+ * if -1, cpcb->pcb_rw[0] holds user registers for rett window
+ * from an earlier T_RWRET pseudo-trap.
+ */
+ sethi %hi(_C_LABEL(cpcb)), %l6
+ ld [%l6 + %lo(_C_LABEL(cpcb))], %l6
+ ld [%l6 + PCB_NSAVED], %l7
+ tst %l7
+ bz,a rft_user
+ sethi %hi(_C_LABEL(want_ast)), %l7 ! first instr of rft_user
+
+ bg,a softtrap ! if (pcb_nsaved > 0)
+ mov T_WINOF, %o0 ! trap(T_WINOF);
+
+ /*
+ * To get here, we must have tried to return from a previous
+ * trap and discovered that it would cause a window underflow.
+ * We then must have tried to pull the registers out of the
+ * user stack (from the address in %fp==%i6) and discovered
+ * that it was either unaligned or not loaded in memory, and
+ * therefore we ran a trap(T_RWRET), which loaded one set of
+ * registers into cpcb->pcb_pcb_rw[0] (if it had killed the
+ * process due to a bad stack, we would not be here).
+ *
+ * We want to load pcb_rw[0] into the previous window, which
+ * we know is currently invalid. In other words, we want
+ * %wim to be 1 << ((cwp + 2) % nwindows).
+ */
+ wr %g0, 0, %wim ! enable restores
+ mov %g6, %l3 ! save g6 in l3
+ mov %l6, %g6 ! set g6 = &u
+ st %g0, [%g6 + PCB_NSAVED] ! clear cpcb->pcb_nsaved
+ restore ! enter window I
+ restore %g0, 1, %l1 ! enter window X, then %l1 = 1
+ rd %psr, %l0
+ sll %l1, %l0, %l1 ! %wim = 1 << CWP;
+ wr %l1, 0, %wim
+ and %l0, 31, %l0
+ st %l0, [%g6 + PCB_WIM] ! cpcb->pcb_wim = CWP;
+ nop ! unnecessary? old wim was 0...
+ save %g0, %g0, %g0 ! back to window I
+
+ !! StackGhost Decrypt (PCB)
+ ! pcb already deferenced in %g6
+ ld [%g6 + PCB_WCOOKIE], %l0 ! get window cookie
+ ldd [%g6 + PCB_RW + 56], %i6 ! get saved return pointer
+ xor %l0, %i7, %i7 ! remove cookie
+
+ !LOADWIN(%g6 + PCB_RW)
+ ldd [%g6 + PCB_RW], %l0
+ ldd [%g6 + PCB_RW + 8], %l2
+ ldd [%g6 + PCB_RW + 16], %l4
+ ldd [%g6 + PCB_RW + 24], %l6
+ ldd [%g6 + PCB_RW + 32], %i0
+ ldd [%g6 + PCB_RW + 40], %i2
+ ldd [%g6 + PCB_RW + 48], %i4
+
+ save %g0, %g0, %g0 ! back to window T (trap window)
+ wr %l0, 0, %psr ! cond codes, cond codes everywhere
+ nop; nop
+ mov %l3, %g6 ! restore g6
+ RETT
+
+! exported end marker for kernel gdb
+ .globl _C_LABEL(endtrapcode)
+_C_LABEL(endtrapcode):
+
+/*
+ * init_tables(nwin) int nwin;
+ *
+ * Set up the uwtab and wmask tables.
+ * We know nwin > 1.
+ */
+init_tables:
+ /*
+ * for (i = -nwin, j = nwin - 2; ++i < 0; j--)
+ * uwtab[i] = j;
+ * (loop runs at least once)
+ */
+ set uwtab, %o3
+ sub %g0, %o0, %o1 ! i = -nwin + 1
+ inc %o1
+ add %o0, -2, %o2 ! j = nwin - 2;
+0:
+ stb %o2, [%o3 + %o1] ! uwtab[i] = j;
+1:
+ inccc %o1 ! ++i < 0?
+ bl 0b ! yes, continue loop
+ dec %o2 ! in any case, j--
+
+ /*
+ * (i now equals 0)
+ * for (j = nwin - 1; i < nwin; i++, j--)
+ * uwtab[i] = j;
+ * (loop runs at least twice)
+ */
+ sub %o0, 1, %o2 ! j = nwin - 1
+0:
+ stb %o2, [%o3 + %o1] ! uwtab[i] = j
+ inc %o1 ! i++
+1:
+ cmp %o1, %o0 ! i < nwin?
+ bl 0b ! yes, continue
+ dec %o2 ! in any case, j--
+
+ /*
+ * We observe that, for i in 0..nwin-2, (i+1)%nwin == i+1;
+ * for i==nwin-1, (i+1)%nwin == 0.
+ * To avoid adding 1, we run i from 1 to nwin and set
+ * wmask[i-1].
+ *
+ * for (i = j = 1; i < nwin; i++) {
+ * j <<= 1; (j now == 1 << i)
+ * wmask[i - 1] = j;
+ * }
+ * (loop runs at least once)
+ */
+ set wmask - 1, %o3
+ mov 1, %o1 ! i = 1;
+ mov 2, %o2 ! j = 2;
+0:
+ stb %o2, [%o3 + %o1] ! (wmask - 1)[i] = j;
+ inc %o1 ! i++
+ cmp %o1, %o0 ! i < nwin?
+ bl,a 0b ! yes, continue
+ sll %o2, 1, %o2 ! (and j <<= 1)
+
+ /*
+ * Now i==nwin, so we want wmask[i-1] = 1.
+ */
+ mov 1, %o2 ! j = 1;
+ retl
+ stb %o2, [%o3 + %o1] ! (wmask - 1)[i] = j;
+
+/* ========================================================================== */
+
+ .align 8 /* to be sure we won't cross a page boundary */
+ENTRY(masktest)
+ ret
+ nop
+
+#define BLINK(how) \
+ call blink; \
+ mov how, %o0
+
+ENTRY(blink)
+ save %sp, -64, %sp
+ set GLU_BSR, %l1
+ lda [%l1] ASI_PHYS_IO, %l2
+ srl %l2, 24, %l2
+ andn %l2, GBSR_LED_MASK, %l2
+ or %l2, %i0, %l2
+ sll %l2, 24, %l2
+ sta %l2, [%l1] ASI_PHYS_IO
+ ret
+ restore
+
+dostart:
+ /*
+ * Startup.
+ *
+ * We have been loaded in high RAM, in the virtual window 0xfd, but
+ * physical window 0xf0, which is also the segment we are linked at.
+ */
+
+ BLINK(GBSR_LED_AMBER)
+
+#if defined(DDB) || NKSYMS > 0
+ /*
+ * Initialize esym to a non-dangerous value for now.
+ */
+ set _C_LABEL(end), %l0
+ sethi %hi(_C_LABEL(esym)), %l1
+ st %l0, [%l1 + %lo(_C_LABEL(esym))]
+#endif
+
+ /*
+ * We are invoked with a regular frame on stack.
+ */
+ ld [%sp + FRAME_ARGC], %g5 ! argc
+ ld [%sp + FRAME_ARGC + 4], %g6 ! argv
+ ld [%sp + FRAME_ARGC + 8], %g7 ! environ
+
+ /*
+ * Here, we should make sure we are really running on an idt
+ * machine. But we won't, because if we could be loaded correctly,
+ * and did not fault earlier, chances are good we are running on
+ * the adequate hardware...
+ */
+
+ /* disable external interrupts - this also disable soft interrupts */
+ set GLU_ICR, %o0
+ mov GICR_DISABLE_ALL, %o1
+ sll %o1, 24, %o1
+ sta %o1, [%o0] ASI_PHYS_IO
+ nop
+
+ CLEAR_FCR
+
+ /* disable interrupts, and enable traps */
+ rd %psr, %o0
+ or %o0, PSR_PIL, %o0
+ wr %o0, %psr
+ nop; nop; nop;
+ or %o0, PSR_ET, %o0
+ wr %o0, %psr
+ nop; nop; nop
+
+ /*
+ * Step 1: Reset the MMU to sane defaults.
+ * We do not disable it because we will be playing with the stack,
+ * which will not be addressable if translations are disabled.
+ */
+
+ sta %g0, [%g0] ASI_PIID
+ sta %g0, [%g0] ASI_GTLB_INVAL_PID
+ nop; nop; nop
+ lda [%g0] ASI_FVAR, %g0
+ lda [%g0] ASI_FPAR, %g0
+ lda [%g0] ASI_FPSR, %g0
+ sta %g0, [%g0] ASI_PID
+
+ rd %psr, %g3 ! paranoia: make sure ...
+ andn %g3, PSR_ET, %g3 ! we have traps off
+ wr %g3, 0, %psr ! so that we can fiddle safely
+ nop; nop; nop
+
+ wr %g0, 0, %wim ! make sure we can set psr
+ nop; nop; nop
+ wr %g0, PSR_S|PSR_PS|PSR_PIL, %psr ! set initial psr
+ nop; nop; nop
+
+ wr %g0, 2, %wim ! set initial %wim (w1 invalid)
+ mov 1, %g1 ! set pcb_wim (log2(%wim) = 1)
+ sethi %hi(_C_LABEL(u0) + PCB_WIM), %g2
+ st %g1, [%g2 + %lo(_C_LABEL(u0) + PCB_WIM)]
+
+ set USRSTACK - CCFSZ, %fp ! as if called from user code
+ set estack0 - CCFSZ - 80, %sp ! via syscall(boot_me_up) or somesuch
+ rd %psr, %l0
+ wr %l0, PSR_ET, %psr
+ nop; nop; nop
+
+ /*
+ * Step 2: clear BSS. This may just be paranoia; the boot
+ * loader might already do it for us; but what the hell.
+ */
+ set _C_LABEL(edata), %o0 ! bzero(edata, end - edata)
+ set _C_LABEL(end), %o1
+ call _C_LABEL(bzero)
+ sub %o1, %o0, %o1
+
+ /*
+ * Stash prom variables now, after bzero, as they live in bss
+ * (which we just zeroed).
+ * This depends on the fact that bzero does not use %g5-%g7.
+ */
+ sethi %hi(_C_LABEL(prom_argc)), %o0
+ st %g5, [%o0 + %lo(_C_LABEL(prom_argc))]
+ sethi %hi(_C_LABEL(prom_argv)), %o0
+ st %g6, [%o0 + %lo(_C_LABEL(prom_argv))]
+ sethi %hi(_C_LABEL(prom_environ)), %o0
+ st %g7, [%o0 + %lo(_C_LABEL(prom_environ))]
+
+ /*
+ * Step 3: compute number of windows and set up tables.
+ * We could do some of this later.
+ */
+ save %sp, -64, %sp
+ rd %psr, %g1
+ restore
+ and %g1, 31, %g1 ! want just the CWP bits
+ add %g1, 1, %o0 ! compute nwindows
+ sethi %hi(_C_LABEL(nwindows)), %o1 ! may as well tell everyone
+ call init_tables
+ st %o0, [%o1 + %lo(_C_LABEL(nwindows))]
+
+ /*
+ * Invoke early C code, still using the PROM trap, so that it can
+ * do proper TLB insertion for us while we are accessing various
+ * onboard devices.
+ */
+
+ call _C_LABEL(bootstrap)
+ nop
+
+ /*
+ * Step 4: change the trap base register, now that our trap handlers
+ * will function (they need the tables we just set up).
+ */
+ set trapbase_kap, %g6
+ wr %g6, 0, %tbr
+ nop; nop; nop ! paranoia
+
+ /*
+ * Step 5: activate our translations...
+ */
+ set _C_LABEL(kernel_pmap_store), %o0
+ ld [%o0 + PMAP_PSEGTAB], %o1
+ sta %o1, [%g0] ASI_PDBR
+ nop; nop; nop
+
+ /*
+ * ... and set up the PTW as we want them
+ */
+ set PTW0_DEFAULT & ~PTW_V, %o1
+ sta %o1, [%g0] ASI_PTW0
+ set PTW1_DEFAULT, %o1
+ sta %o1, [%g0] ASI_PTW1
+ set PTW2_DEFAULT, %o1
+ sta %o1, [%g0] ASI_PTW2
+
+ sta %g0, [%g0] ASI_PIID
+ sta %g0, [%g0] ASI_GTLB_INVAL_PID
+ nop; nop; nop
+
+ /*
+ * Call main. This returns to us after loading /sbin/init into
+ * user space. (If the exec fails, main() does not return.)
+ */
+ call _C_LABEL(main)
+ clr %o0 ! our frame arg is ignored
+ /*NOTREACHED*/
+
+
+/*
+ * The following code is copied to the top of the user stack when each
+ * process is exec'ed, and signals are `trampolined' off it.
+ *
+ * When this code is run, the stack looks like:
+ * [%sp] 64 bytes to which registers can be dumped
+ * [%sp + 64] signal number (goes in %o0)
+ * [%sp + 64 + 4] siginfo_t pointer (goes in %o1)
+ * [%sp + 64 + 8] sigcontext pointer (goes in %o2)
+ * [%sp + 64 + 12] argument for %o3, currently unsupported (always 0)
+ * [%sp + 64 + 16] first word of saved state (sigcontext)
+ * .
+ * .
+ * .
+ * [%sp + NNN] last word of saved state
+ * (followed by previous stack contents or top of signal stack).
+ * The address of the function to call is in %g1; the old %g1 and %o0
+ * have already been saved in the sigcontext. We are running in a clean
+ * window, all previous windows now being saved to the stack.
+ *
+ * Note that [%sp + 64 + 8] == %sp + 64 + 16. The copy at %sp+64+8
+ * will eventually be removed, with a hole left in its place, if things
+ * work out.
+ */
+ .globl _C_LABEL(sigcode)
+ .globl _C_LABEL(esigcode)
+_C_LABEL(sigcode):
+ /*
+ * XXX the `save' and `restore' below are unnecessary: should
+ * replace with simple arithmetic on %sp
+ *
+ * Make room on the stack for 32 %f registers + %fsr. This comes
+ * out to 33*4 or 132 bytes, but this must be aligned to a multiple
+ * of 8, or 136 bytes.
+ */
+ save %sp, -CCFSZ - 136, %sp
+ mov %g2, %l2 ! save globals in %l registers
+ mov %g3, %l3
+ mov %g4, %l4
+ mov %g5, %l5
+ mov %g6, %l6
+ mov %g7, %l7
+ /*
+ * Saving the fpu registers is expensive, so do it iff the fsr
+ * stored in the sigcontext shows that the fpu is enabled.
+ */
+ ld [%fp + 64 + 16 + SC_PSR_OFFSET], %l0
+ sethi %hi(PSR_EF), %l1 ! FPU enable bit is too high for andcc
+ andcc %l0, %l1, %l0 ! %l0 = fpu enable bit
+ be 1f ! if not set, skip the saves
+ rd %y, %l1 ! in any case, save %y
+
+ ! fpu is enabled, oh well
+ st %fsr, [%sp + CCFSZ + 0]
+ std %f0, [%sp + CCFSZ + 8]
+ std %f2, [%sp + CCFSZ + 16]
+ std %f4, [%sp + CCFSZ + 24]
+ std %f6, [%sp + CCFSZ + 32]
+ std %f8, [%sp + CCFSZ + 40]
+ std %f10, [%sp + CCFSZ + 48]
+ std %f12, [%sp + CCFSZ + 56]
+ std %f14, [%sp + CCFSZ + 64]
+ std %f16, [%sp + CCFSZ + 72]
+ std %f18, [%sp + CCFSZ + 80]
+ std %f20, [%sp + CCFSZ + 88]
+ std %f22, [%sp + CCFSZ + 96]
+ std %f24, [%sp + CCFSZ + 104]
+ std %f26, [%sp + CCFSZ + 112]
+ std %f28, [%sp + CCFSZ + 120]
+ std %f30, [%sp + CCFSZ + 128]
+
+1:
+ ldd [%fp + 64], %o0 ! sig, sip
+ ld [%fp + 76], %o3 ! arg3
+#ifdef SIG_DEBUG
+ subcc %o0, 32, %g0 ! signals are 1-32
+ bgu _C_LABEL(suicide)
+ nop
+#endif
+ call %g1 ! (*sa->sa_handler)(sig,sip,scp,arg3)
+ add %fp, 64 + 16, %o2 ! scp
+
+ /*
+ * Now that the handler has returned, re-establish all the state
+ * we just saved above, then do a sigreturn.
+ */
+ tst %l0 ! reload fpu registers?
+ be 1f ! if not, skip the loads
+ wr %l1, %g0, %y ! in any case, restore %y
+
+ ld [%sp + CCFSZ + 0], %fsr
+ ldd [%sp + CCFSZ + 8], %f0
+ ldd [%sp + CCFSZ + 16], %f2
+ ldd [%sp + CCFSZ + 24], %f4
+ ldd [%sp + CCFSZ + 32], %f6
+ ldd [%sp + CCFSZ + 40], %f8
+ ldd [%sp + CCFSZ + 48], %f10
+ ldd [%sp + CCFSZ + 56], %f12
+ ldd [%sp + CCFSZ + 64], %f14
+ ldd [%sp + CCFSZ + 72], %f16
+ ldd [%sp + CCFSZ + 80], %f18
+ ldd [%sp + CCFSZ + 88], %f20
+ ldd [%sp + CCFSZ + 96], %f22
+ ldd [%sp + CCFSZ + 104], %f24
+ ldd [%sp + CCFSZ + 112], %f26
+ ldd [%sp + CCFSZ + 120], %f28
+ ldd [%sp + CCFSZ + 128], %f30
+
+1:
+ mov %l2, %g2
+ mov %l3, %g3
+ mov %l4, %g4
+ mov %l5, %g5
+ mov %l6, %g6
+ mov %l7, %g7
+
+ restore %g0, SYS_sigreturn, %g1 ! get registers back & set syscall #
+ add %sp, 64 + 16, %o0 ! compute scp
+ t ST_SYSCALL ! sigreturn(scp)
+ ! sigreturn does not return unless it fails
+ mov SYS_exit, %g1 ! exit(errno)
+ t ST_SYSCALL
+
+#ifdef SIG_DEBUG
+ .globl _C_LABEL(suicide)
+_C_LABEL(suicide):
+ mov 139, %g1 ! obsolete syscall, puke...
+ t ST_SYSCALL
+#endif
+_C_LABEL(esigcode):
+
+#ifdef COMPAT_SVR4
+/*
+ * The following code is copied to the top of the user stack when each
+ * process is exec'ed, and signals are `trampolined' off it.
+ *
+ * When this code is run, the stack looks like:
+ * [%sp] 64 bytes to which registers can be dumped
+ * [%sp + 64] signal number (goes in %o0)
+ * [%sp + 64 + 4] pointer to saved siginfo
+ * [%sp + 64 + 8] pointer to saved context
+ * [%sp + 64 + 12] address of the user's handler
+ * [%sp + 64 + 16] first word of saved state (context)
+ * .
+ * .
+ * .
+ * [%sp + NNN] last word of saved state (siginfo)
+ * (followed by previous stack contents or top of signal stack).
+ * The address of the function to call is in %g1; the old %g1 and %o0
+ * have already been saved in the sigcontext. We are running in a clean
+ * window, all previous windows now being saved to the stack.
+ *
+ * Note that [%sp + 64 + 8] == %sp + 64 + 16. The copy at %sp+64+8
+ * will eventually be removed, with a hole left in its place, if things
+ * work out.
+ */
+ .globl _C_LABEL(svr4_sigcode)
+ .globl _C_LABEL(svr4_esigcode)
+_C_LABEL(svr4_sigcode):
+ /*
+ * XXX the `save' and `restore' below are unnecessary: should
+ * replace with simple arithmetic on %sp
+ *
+ * Make room on the stack for 32 %f registers + %fsr. This comes
+ * out to 33*4 or 132 bytes, but this must be aligned to a multiple
+ * of 8, or 136 bytes.
+ */
+ save %sp, -CCFSZ - 136, %sp
+ mov %g2, %l2 ! save globals in %l registers
+ mov %g3, %l3
+ mov %g4, %l4
+ mov %g5, %l5
+ mov %g6, %l6
+ mov %g7, %l7
+ /*
+ * Saving the fpu registers is expensive, so do it iff the fsr
+ * stored in the sigcontext shows that the fpu is enabled.
+ */
+ ld [%fp + 64 + 16 + SC_PSR_OFFSET], %l0
+ sethi %hi(PSR_EF), %l1 ! FPU enable bit is too high for andcc
+ andcc %l0, %l1, %l0 ! %l0 = fpu enable bit
+ be 1f ! if not set, skip the saves
+ rd %y, %l1 ! in any case, save %y
+
+ ! fpu is enabled, oh well
+ st %fsr, [%sp + CCFSZ + 0]
+ std %f0, [%sp + CCFSZ + 8]
+ std %f2, [%sp + CCFSZ + 16]
+ std %f4, [%sp + CCFSZ + 24]
+ std %f6, [%sp + CCFSZ + 32]
+ std %f8, [%sp + CCFSZ + 40]
+ std %f10, [%sp + CCFSZ + 48]
+ std %f12, [%sp + CCFSZ + 56]
+ std %f14, [%sp + CCFSZ + 64]
+ std %f16, [%sp + CCFSZ + 72]
+ std %f18, [%sp + CCFSZ + 80]
+ std %f20, [%sp + CCFSZ + 88]
+ std %f22, [%sp + CCFSZ + 96]
+ std %f24, [%sp + CCFSZ + 104]
+ std %f26, [%sp + CCFSZ + 112]
+ std %f28, [%sp + CCFSZ + 120]
+ std %f30, [%sp + CCFSZ + 128]
+
+1:
+ ldd [%fp + 64], %o0 ! sig, siginfo
+ ld [%fp + 72], %o2 ! uctx
+ call %g1 ! (*sa->sa_handler)(sig,siginfo,uctx)
+ nop
+
+ /*
+ * Now that the handler has returned, re-establish all the state
+ * we just saved above, then do a sigreturn.
+ */
+ tst %l0 ! reload fpu registers?
+ be 1f ! if not, skip the loads
+ wr %l1, %g0, %y ! in any case, restore %y
+
+ ld [%sp + CCFSZ + 0], %fsr
+ ldd [%sp + CCFSZ + 8], %f0
+ ldd [%sp + CCFSZ + 16], %f2
+ ldd [%sp + CCFSZ + 24], %f4
+ ldd [%sp + CCFSZ + 32], %f6
+ ldd [%sp + CCFSZ + 40], %f8
+ ldd [%sp + CCFSZ + 48], %f10
+ ldd [%sp + CCFSZ + 56], %f12
+ ldd [%sp + CCFSZ + 64], %f14
+ ldd [%sp + CCFSZ + 72], %f16
+ ldd [%sp + CCFSZ + 80], %f18
+ ldd [%sp + CCFSZ + 88], %f20
+ ldd [%sp + CCFSZ + 96], %f22
+ ldd [%sp + CCFSZ + 104], %f24
+ ldd [%sp + CCFSZ + 112], %f26
+ ldd [%sp + CCFSZ + 120], %f28
+ ldd [%sp + CCFSZ + 128], %f30
+
+1:
+ mov %l2, %g2
+ mov %l3, %g3
+ mov %l4, %g4
+ mov %l5, %g5
+ mov %l6, %g6
+ mov %l7, %g7
+
+ restore %g0, SVR4_SYS_context, %g1 ! get registers & set syscall #
+ mov 1, %o0
+ add %sp, 64 + 16, %o1 ! compute ucontextp
+ t ST_SYSCALL ! svr4_context(1, ucontextp)
+ ! setcontext does not return unless it fails
+ mov SYS_exit, %g1 ! exit(errno)
+ t ST_SYSCALL
+_C_LABEL(svr4_esigcode):
+#endif
+
+/*
+ * Primitives
+ */
+#if 0
+#ifdef GPROF
+ .globl mcount
+#define ENTRY(x) \
+ .globl _C_LABEL(x); _C_LABEL(x): ; \
+ save %sp, -CCFSZ, %sp; \
+ call mcount; \
+ nop; \
+ restore
+#else
+#define ENTRY(x) .globl _C_LABEL(x); _C_LABEL(x):
+#endif
+#endif
+#define ALTENTRY(x) .globl _C_LABEL(x); _C_LABEL(x):
+
+/*
+ * General-purpose NULL routine.
+ */
+ENTRY(sparc_noop)
+ retl
+ nop
+
+/*
+ * getfp() - get stack frame pointer
+ */
+ENTRY(getfp)
+ retl
+ mov %fp, %o0
+
+/*
+ * copyinstr(fromaddr, toaddr, maxlength, &lencopied)
+ *
+ * Copy a null terminated string from the user address space into
+ * the kernel address space.
+ */
+ENTRY(copyinstr)
+ ! %o0 = fromaddr, %o1 = toaddr, %o2 = maxlen, %o3 = &lencopied
+ mov %o1, %o5 ! save = toaddr;
+ tst %o2 ! maxlen == 0?
+ beq,a Lcstoolong0 ! yes, return ENAMETOOLONG
+ sethi %hi(_C_LABEL(cpcb)), %o4
+
+ set VM_MIN_KERNEL_ADDRESS, %o4
+ cmp %o0, %o4 ! fromaddr < VM_MIN_KERNEL_ADDRESS?
+ blu Lcsdocopyi ! yes, go do it
+ sethi %hi(_C_LABEL(cpcb)), %o4 ! (first instr of copy)
+
+ b Lcsdone ! no, return EFAULT
+ mov EFAULT, %o0
+
+/*
+ * copyoutstr(fromaddr, toaddr, maxlength, &lencopied)
+ *
+ * Copy a null terminated string from the kernel
+ * address space to the user address space.
+ */
+ENTRY(copyoutstr)
+ ! %o0 = fromaddr, %o1 = toaddr, %o2 = maxlen, %o3 = &lencopied
+ mov %o1, %o5 ! save = toaddr;
+ tst %o2 ! maxlen == 0?
+ beq,a Lcstoolong0 ! yes, return ENAMETOOLONG
+ sethi %hi(_C_LABEL(cpcb)), %o4
+
+ set VM_MIN_KERNEL_ADDRESS, %o4
+ cmp %o1, %o4 ! toaddr < VM_MIN_KERNEL_ADDRESS?
+ blu Lcsdocopyo ! yes, go do it
+ sethi %hi(_C_LABEL(cpcb)), %o4 ! (first instr of copy)
+
+ b Lcsdone ! no, return EFAULT
+ mov EFAULT, %o0
+
+Lcsdocopyi:
+! sethi %hi(_C_LABEL(cpcb)), %o4 ! (done earlier)
+ ld [%o4 + %lo(_C_LABEL(cpcb))], %o4 ! catch faults
+ set Lcsfaulti, %g1
+ b 0f
+ st %g1, [%o4 + PCB_ONFAULT]
+
+Lcsdocopyo:
+! sethi %hi(_C_LABEL(cpcb)), %o4 ! (done earlier)
+ ld [%o4 + %lo(_C_LABEL(cpcb))], %o4 ! catch faults
+ set Lcsfaulto, %g1
+ st %g1, [%o4 + PCB_ONFAULT]
+
+! XXX should do this in bigger chunks when possible
+0: ! loop:
+ ldsb [%o0], %g1 ! c = *fromaddr;
+ tst %g1
+ stb %g1, [%o1] ! *toaddr++ = c;
+ be 1f ! if (c == NULL)
+ inc %o1 ! goto ok;
+ deccc %o2 ! if (--len > 0) {
+ bgu 0b ! fromaddr++;
+ inc %o0 ! goto loop;
+ ! }
+Lcstoolong: !
+ deccc %o1
+ stb %g0, [%o1] ! *--toaddr = '\0';
+Lcstoolong0: !
+ b Lcsdone ! error = ENAMETOOLONG;
+ mov ENAMETOOLONG, %o0 ! goto done;
+1: ! ok:
+ clr %o0 ! error = 0;
+Lcsdone: ! done:
+ sub %o1, %o5, %o1 ! len = to - save;
+ tst %o3 ! if (lencopied)
+ bnz,a 3f
+ st %o1, [%o3] ! *lencopied = len;
+3:
+ retl ! cpcb->pcb_onfault = 0;
+ st %g0, [%o4 + PCB_ONFAULT]! return (error);
+
+Lcsfaulti:
+ cmp %o1, %o5 ! did we write to the string?
+ be 1f
+ nop
+ deccc %o1 ! --toaddr
+1:
+ stb %g0, [%o1] ! *toaddr = '\0';
+ b Lcsdone ! error = EFAULT;
+ mov EFAULT, %o0 ! goto ret;
+
+Lcsfaulto:
+ cmp %o1, %o5 ! did we write to the string?
+ be 1f
+ nop
+ deccc %o1
+ stb %g0, [%o1] ! *--toaddr = '\0';
+1:
+ b Lcsdone ! error = EFAULT;
+ mov EFAULT, %o0 ! goto ret;
+
+/*
+ * copystr(fromaddr, toaddr, maxlength, &lencopied)
+ *
+ * Copy a null terminated string from one point to another in
+ * the kernel address space. (This is a leaf procedure, but
+ * it does not seem that way to the C compiler.)
+ */
+ENTRY(copystr)
+ mov %o1, %o5 ! to0 = to;
+ tst %o2 ! if (maxlength == 0)
+ beq,a 2f !
+ mov ENAMETOOLONG, %o0 ! ret = ENAMETOOLONG; goto done;
+
+0: ! loop:
+ ldsb [%o0], %o4 ! c = *from;
+ tst %o4
+ stb %o4, [%o1] ! *to++ = c;
+ be 1f ! if (c == 0)
+ inc %o1 ! goto ok;
+ deccc %o2 ! if (--len > 0) {
+ bgu,a 0b ! from++;
+ inc %o0 ! goto loop;
+ b 2f ! }
+ mov ENAMETOOLONG, %o0 ! ret = ENAMETOOLONG; goto done;
+1: ! ok:
+ clr %o0 ! ret = 0;
+2:
+ sub %o1, %o5, %o1 ! len = to - to0;
+ tst %o3 ! if (lencopied)
+ bnz,a 3f
+ st %o1, [%o3] ! *lencopied = len;
+3:
+ retl
+ nop
+
+/*
+ * Copyin(src, dst, len)
+ *
+ * Copy specified amount of data from user space into the kernel.
+ */
+ENTRY(copyin)
+ set VM_MIN_KERNEL_ADDRESS, %o3
+ cmp %o0, %o3 ! src < VM_MIN_KERNEL_ADDRESS?
+ blu,a Ldocopy ! yes, can try it
+ sethi %hi(_C_LABEL(cpcb)), %o3
+
+ /* source address points into kernel space: return EFAULT */
+ retl
+ mov EFAULT, %o0
+
+/*
+ * Copyout(src, dst, len)
+ *
+ * Copy specified amount of data from kernel to user space.
+ * Just like copyin, except that the `dst' addresses are user space
+ * rather than the `src' addresses.
+ */
+ENTRY(copyout)
+ set VM_MIN_KERNEL_ADDRESS, %o3
+ cmp %o1, %o3 ! dst < VM_MIN_KERNEL_ADDRESS?
+ blu,a Ldocopy
+ sethi %hi(_C_LABEL(cpcb)), %o3
+
+ /* destination address points into kernel space: return EFAULT */
+ retl
+ mov EFAULT, %o0
+
+ /*
+ * ******NOTE****** this depends on bcopy() not using %g7
+ */
+Ldocopy:
+! sethi %hi(_C_LABEL(cpcb)), %o3
+ ld [%o3 + %lo(_C_LABEL(cpcb))], %o3
+ set Lcopyfault, %o4
+ mov %o7, %g7 ! save return address
+ call _C_LABEL(bcopy) ! bcopy(src, dst, len)
+ st %o4, [%o3 + PCB_ONFAULT]
+
+ sethi %hi(_C_LABEL(cpcb)), %o3
+ ld [%o3 + %lo(_C_LABEL(cpcb))], %o3
+ st %g0, [%o3 + PCB_ONFAULT]
+ jmp %g7 + 8
+ clr %o0 ! return 0
+
+! Copyin or copyout fault. Clear cpcb->pcb_onfault and return EFAULT.
+! Note that although we were in bcopy, there is no state to clean up;
+! the only special thing is that we have to return to [g7 + 8] rather than
+! [o7 + 8].
+Lcopyfault:
+ sethi %hi(_C_LABEL(cpcb)), %o3
+ ld [%o3 + %lo(_C_LABEL(cpcb))], %o3
+ st %g0, [%o3 + PCB_ONFAULT]
+ jmp %g7 + 8
+ mov EFAULT, %o0
+
+
+/*
+ * Write all user windows presently in the CPU back to the user's stack.
+ * We just do `save' instructions until pcb_uw == 0.
+ *
+ * p = cpcb;
+ * nsaves = 0;
+ * while (p->pcb_uw > 0)
+ * save(), nsaves++;
+ * while (--nsaves >= 0)
+ * restore();
+ */
+ENTRY(write_user_windows)
+ sethi %hi(_C_LABEL(cpcb)), %g6
+ ld [%g6 + %lo(_C_LABEL(cpcb))], %g6
+ b 2f
+ clr %g5
+1:
+ save %sp, -64, %sp
+2:
+ ld [%g6 + PCB_UW], %g7
+ tst %g7
+ bg,a 1b
+ inc %g5
+3:
+ deccc %g5
+ bge,a 3b
+ restore
+ retl
+ nop
+
+
+ .comm _C_LABEL(want_resched),4
+/*
+ * Masterpaddr is the p->p_addr of the last process on the processor.
+ * XXX masterpaddr is almost the same as cpcb
+ * XXX should delete this entirely
+ */
+ .comm _C_LABEL(masterpaddr), 4
+
+/*
+ * Switch statistics (for later tweaking):
+ * nswitchdiff = p1 => p2 (i.e., chose different process)
+ * nswitchexit = number of calls to switchexit()
+ * _cnt.v_swtch = total calls to swtch+swtchexit
+ */
+ .comm _C_LABEL(nswitchdiff), 4
+ .comm _C_LABEL(nswitchexit), 4
+
+/*
+ * REGISTER USAGE IN cpu_switch AND switchexit:
+ * This is split into two phases, more or less
+ * `before we locate a new proc' and `after'.
+ * Some values are the same in both phases.
+ * Note that the %o0-registers are not preserved across
+ * the psr change when entering a new process, since this
+ * usually changes the CWP field (hence heavy usage of %g's).
+ *
+ * %g1 = oldpsr (excluding ipl bits)
+ * %g2 = %hi(_C_LABEL(whichqs)); newpsr
+ * %g3 = p
+ * %g4 = lastproc
+ * %g5 = <free>; newpcb
+ * %g6 = %hi(_C_LABEL(cpcb))
+ * %g7 = %hi(_C_LABEL(curproc))
+ * %o0 = tmp 1
+ * %o1 = tmp 2
+ * %o2 = tmp 3
+ * %o3 = tmp 4; whichqs; vm
+ * %o4 = tmp 4; which; sswap
+ * %o5 = tmp 5; q; <free>
+ */
+
+/*
+ * switchexit is called only from cpu_exit() before the current process
+ * has freed its kernel stack; we must free it. (curproc is already NULL.)
+ *
+ * We lay the process to rest by changing to the `idle' kernel stack,
+ * and note that the `last loaded process' is nonexistent.
+ */
+ENTRY(switchexit)
+ mov %o0, %g2 ! save proc for exit2() call
+
+ /*
+ * Change pcb to idle u. area, i.e., set %sp to top of stack
+ * and %psr to PSR_S|PSR_ET, and set cpcb to point to _idle_u.
+ * Once we have left the old stack, we can call kmem_free to
+ * destroy it. Call it any sooner and the register windows
+ * go bye-bye.
+ */
+ set _C_LABEL(idle_u), %g5
+ sethi %hi(_C_LABEL(cpcb)), %g6
+ mov 1, %g7
+ wr %g0, PSR_S, %psr ! change to window 0, traps off
+ nop; nop; nop
+ wr %g0, 2, %wim ! and make window 1 the trap window
+ st %g5, [%g6 + %lo(_C_LABEL(cpcb))] ! cpcb = &idle_u
+ st %g7, [%g5 + PCB_WIM] ! idle_u.pcb_wim = log2(2) = 1
+ set _C_LABEL(idle_u) + USPACE-CCFSZ, %sp ! set new %sp
+#ifdef DEBUG
+ set _C_LABEL(idle_u), %l6
+ SET_SP_REDZONE(%l6, %l5)
+#endif
+ wr %g0, PSR_S|PSR_ET, %psr ! and then enable traps
+ call _C_LABEL(exit2) ! exit2(p)
+ mov %g2, %o0
+
+ /*
+ * Now fall through to `the last switch'. %g6 was set to
+ * %hi(_C_LABEL(cpcb)), but may have been clobbered in kmem_free,
+ * so all the registers described below will be set here.
+ *
+ * REGISTER USAGE AT THIS POINT:
+ * %g1 = oldpsr (excluding ipl bits)
+ * %g2 = %hi(_C_LABEL(whichqs))
+ * %g4 = lastproc
+ * %g6 = %hi(_C_LABEL(cpcb))
+ * %g7 = %hi(_C_LABEL(curproc))
+ * %o0 = tmp 1
+ * %o1 = tmp 2
+ * %o3 = whichqs
+ */
+
+ INCR(_C_LABEL(nswitchexit)) ! nswitchexit++;
+ INCR(_C_LABEL(uvmexp)+V_SWTCH) ! cnt.v_switch++;
+
+ mov PSR_S|PSR_ET, %g1 ! oldpsr = PSR_S | PSR_ET;
+ sethi %hi(_C_LABEL(whichqs)), %g2
+ clr %g4 ! lastproc = NULL;
+ sethi %hi(_C_LABEL(cpcb)), %g6
+ sethi %hi(_C_LABEL(curproc)), %g7
+ /* FALLTHROUGH */
+
+/*
+ * When no processes are on the runq, switch
+ * idles here waiting for something to come ready.
+ * The registers are set up as noted above.
+ */
+ .globl idle
+idle:
+ st %g0, [%g7 + %lo(_C_LABEL(curproc))] ! curproc = NULL;
+ wr %g1, 0, %psr ! (void) spl0();
+1: ! spin reading _whichqs until nonzero
+ ld [%g2 + %lo(_C_LABEL(whichqs))], %o3
+ tst %o3
+ bnz,a Lsw_scan
+ wr %g1, IPL_CLOCK << 8, %psr ! (void) splclock();
+ b,a 1b
+
+Lsw_panic_rq:
+ sethi %hi(1f), %o0
+ call _C_LABEL(panic)
+ or %lo(1f), %o0, %o0
+Lsw_panic_wchan:
+ sethi %hi(2f), %o0
+ call _C_LABEL(panic)
+ or %lo(2f), %o0, %o0
+Lsw_panic_srun:
+ sethi %hi(3f), %o0
+ call _C_LABEL(panic)
+ or %lo(3f), %o0, %o0
+1: .asciz "switch rq"
+2: .asciz "switch wchan"
+3: .asciz "switch SRUN"
+ _ALIGN
+
+/*
+ * cpu_switch() picks a process to run and runs it, saving the current
+ * one away. On the assumption that (since most workstations are
+ * single user machines) the chances are quite good that the new
+ * process will turn out to be the current process, we defer saving
+ * it here until we have found someone to load. If that someone
+ * is the current process we avoid both store and load.
+ *
+ * cpu_switch() is always entered at splstatclock or splhigh.
+ *
+ * IT MIGHT BE WORTH SAVING BEFORE ENTERING idle TO AVOID HAVING TO
+ * SAVE LATER WHEN SOMEONE ELSE IS READY ... MUST MEASURE!
+ */
+ .globl _C_LABEL(time)
+ENTRY(cpu_switch)
+ /*
+ * REGISTER USAGE AT THIS POINT:
+ * %g1 = oldpsr (excluding ipl bits)
+ * %g2 = %hi(_C_LABEL(whichqs))
+ * %g3 = p
+ * %g4 = lastproc
+ * %g5 = tmp 0
+ * %g6 = %hi(_C_LABEL(cpcb))
+ * %g7 = %hi(_C_LABEL(curproc))
+ * %o0 = tmp 1
+ * %o1 = tmp 2
+ * %o2 = tmp 3
+ * %o3 = tmp 4, then at Lsw_scan, whichqs
+ * %o4 = tmp 5, then at Lsw_scan, which
+ * %o5 = tmp 6, then at Lsw_scan, q
+ */
+ sethi %hi(_C_LABEL(whichqs)), %g2 ! set up addr regs
+ sethi %hi(_C_LABEL(cpcb)), %g6
+ ld [%g6 + %lo(_C_LABEL(cpcb))], %o0
+ std %o6, [%o0 + PCB_SP] ! cpcb->pcb_<sp,pc> = <sp,pc>;
+ rd %psr, %g1 ! oldpsr = %psr;
+ sethi %hi(_C_LABEL(curproc)), %g7
+ ld [%g7 + %lo(_C_LABEL(curproc))], %g4 ! lastproc = curproc;
+ st %g1, [%o0 + PCB_PSR] ! cpcb->pcb_psr = oldpsr;
+ andn %g1, PSR_PIL, %g1 ! oldpsr &= ~PSR_PIL;
+
+ /*
+ * In all the fiddling we did to get this far, the thing we are
+ * waiting for might have come ready, so let interrupts in briefly
+ * before checking for other processes. Note that we still have
+ * curproc set---we have to fix this or we can get in trouble with
+ * the run queues below.
+ */
+ st %g0, [%g7 + %lo(_C_LABEL(curproc))] ! curproc = NULL;
+ wr %g1, 0, %psr ! (void) spl0();
+ nop; nop; nop ! paranoia
+ wr %g1, IPL_CLOCK << 8 , %psr ! (void) splclock();
+
+Lsw_scan:
+ nop; nop; nop ! paranoia
+ ld [%g2 + %lo(_C_LABEL(whichqs))], %o3
+
+ /*
+ * Optimized inline expansion of `which = ffs(whichqs) - 1';
+ * branches to idle if ffs(whichqs) was 0.
+ */
+ set ffstab, %o2
+ andcc %o3, 0xff, %o1 ! byte 0 zero?
+ bz,a 1f ! yes, try byte 1
+ srl %o3, 8, %o0
+ b 2f ! ffs = ffstab[byte0]; which = ffs - 1;
+ ldsb [%o2 + %o1], %o0
+1: andcc %o0, 0xff, %o1 ! byte 1 zero?
+ bz,a 1f ! yes, try byte 2
+ srl %o0, 8, %o0
+ ldsb [%o2 + %o1], %o0 ! which = ffstab[byte1] + 7;
+ b 3f
+ add %o0, 7, %o4
+1: andcc %o0, 0xff, %o1 ! byte 2 zero?
+ bz,a 1f ! yes, try byte 3
+ srl %o0, 8, %o0
+ ldsb [%o2 + %o1], %o0 ! which = ffstab[byte2] + 15;
+ b 3f
+ add %o0, 15, %o4
+1: ldsb [%o2 + %o0], %o0 ! ffs = ffstab[byte3] + 24
+ addcc %o0, 24, %o0 ! (note that ffstab[0] == -24)
+ bz idle ! if answer was 0, go idle
+ EMPTY
+2: sub %o0, 1, %o4 ! which = ffs(whichqs) - 1
+3: /* end optimized inline expansion */
+
+ /*
+ * We found a nonempty run queue. Take its first process.
+ */
+ set _C_LABEL(qs), %o5 ! q = &qs[which];
+ sll %o4, 3, %o0
+ add %o0, %o5, %o5
+ ld [%o5], %g3 ! p = q->ph_link;
+ cmp %g3, %o5 ! if (p == q)
+ be Lsw_panic_rq ! panic("switch rq");
+ EMPTY
+ ld [%g3], %o0 ! tmp0 = p->p_forw;
+ st %o0, [%o5] ! q->ph_link = tmp0;
+ st %o5, [%o0 + 4] ! tmp0->p_back = q;
+ cmp %o0, %o5 ! if (tmp0 == q)
+ bne 1f
+ EMPTY
+ mov 1, %o1 ! whichqs &= ~(1 << which);
+ sll %o1, %o4, %o1
+ andn %o3, %o1, %o3
+ st %o3, [%g2 + %lo(_C_LABEL(whichqs))]
+1:
+ /*
+ * PHASE TWO: NEW REGISTER USAGE:
+ * %g1 = oldpsr (excluding ipl bits)
+ * %g2 = newpsr
+ * %g3 = p
+ * %g4 = lastproc
+ * %g5 = newpcb
+ * %g6 = %hi(_C_LABEL(cpcb))
+ * %g7 = %hi(_C_LABEL(curproc))
+ * %o0 = tmp 1
+ * %o1 = tmp 2
+ * %o2 = tmp 3
+ * %o3 = vm
+ * %o4 = sswap
+ * %o5 = <free>
+ */
+
+ /* firewalls */
+ ld [%g3 + P_WCHAN], %o0 ! if (p->p_wchan)
+ tst %o0
+ bne Lsw_panic_wchan ! panic("switch wchan");
+ EMPTY
+ ldsb [%g3 + P_STAT], %o0 ! if (p->p_stat != SRUN)
+ cmp %o0, SRUN
+ bne Lsw_panic_srun ! panic("switch SRUN");
+ EMPTY
+
+ /*
+ * Committed to running process p.
+ * It may be the same as the one we were running before.
+ */
+ mov SONPROC, %o0 ! p->p_stat = SONPROC
+ stb %o0, [%g3 + P_STAT]
+ sethi %hi(_C_LABEL(want_resched)), %o0
+ st %g0, [%o0 + %lo(_C_LABEL(want_resched))] ! want_resched = 0;
+ ld [%g3 + P_ADDR], %g5 ! newpcb = p->p_addr;
+ st %g0, [%g3 + 4] ! p->p_back = NULL;
+ ld [%g5 + PCB_PSR], %g2 ! newpsr = newpcb->pcb_psr;
+ st %g3, [%g7 + %lo(_C_LABEL(curproc))] ! curproc = p;
+
+ cmp %g3, %g4 ! p == lastproc?
+ be,a Lsw_sameproc ! yes, go return 0
+ wr %g2, 0, %psr ! (after restoring ipl)
+
+ /*
+ * Not the old process. Save the old process, if any;
+ * then load p.
+ */
+ tst %g4
+ be,a Lsw_load ! if no old process, go load
+ wr %g1, (IPL_CLOCK << 8) | PSR_ET, %psr
+
+ INCR(_C_LABEL(nswitchdiff)) ! clobbers %o0,%o1
+ /*
+ * save: write back all windows (including the current one).
+ * XXX crude; knows nwindows <= 8
+ */
+#define SAVE save %sp, -64, %sp
+wb1: SAVE; SAVE; SAVE; SAVE; SAVE; SAVE; SAVE /* 7 of each: */
+ restore; restore; restore; restore; restore; restore; restore
+
+ /*
+ * Load the new process. To load, we must change stacks and
+ * alter cpcb and %wim, hence we must disable traps. %psr is
+ * currently equal to oldpsr (%g1) ^ (IPL_CLOCK << 8);
+ * this means that PSR_ET is on. Likewise, PSR_ET is on
+ * in newpsr (%g2), although we do not know newpsr's ipl.
+ *
+ * We also must load up the `in' and `local' registers.
+ */
+ wr %g1, (IPL_CLOCK << 8) | PSR_ET, %psr
+Lsw_load:
+! wr %g1, (IPL_CLOCK << 8) | PSR_ET, %psr ! done above
+ /* compute new wim */
+ ld [%g5 + PCB_WIM], %o0
+ mov 1, %o1
+ sll %o1, %o0, %o0
+ wr %o0, 0, %wim ! %wim = 1 << newpcb->pcb_wim;
+ /* now must not change %psr for 3 more instrs */
+/*1*/ set PSR_EF|PSR_EC, %o0
+/*2*/ andn %g2, %o0, %g2 ! newpsr &= ~(PSR_EF|PSR_EC);
+/*3*/ nop
+ /* set new psr, but with traps disabled */
+ wr %g2, PSR_ET, %psr ! %psr = newpsr ^ PSR_ET;
+ /* set new cpcb */
+ st %g5, [%g6 + %lo(_C_LABEL(cpcb))] ! cpcb = newpcb;
+
+ /*
+ * Now we need to set up the new translations; but we have to be
+ * careful not to fault (TLB miss) while dereferencing the proper
+ * structures.
+ */
+
+ ld [%g3 + P_VMSPACE], %o3 ! vm = p->p_vmspace;
+ PTE_OF_ADDR(%o3, %o1, %o2, badstack)
+ INSERT_PTE(%o3, %o1)
+
+ ld [%o3 + VM_PMAP], %o3 ! pm = vm->vm_map.pmap;
+ add %o3, PMAP_PSEGTAB, %o3
+ PTE_OF_ADDR(%o3, %o1, %o2, badstack)
+ INSERT_PTE(%o3, %o1)
+
+ ld [%o3], %o3 ! pmap->pm_psegtab
+ lda [%g0] ASI_PDBR, %o4 ! get old psegtab
+ ldd [%g5 + PCB_SP], %o6 ! <sp,pc> = newpcb->pcb_<sp,pc>
+
+ /* skip flushes if no PDBR change */
+ cmp %o3, %o4
+ be 9f
+ clr %o0
+
+ /* flush cache */
+ lda [%g0] ASI_MMCR, %o2
+ andn %o2, MMCR_DSET0 | MMCR_DSET1, %o2
+
+ or %o2, MMCR_DSET0, %o1 ! flush DSET 0
+ sta %o1, [%g0] ASI_MMCR
+ mov DCACHE_LINE(DCACHE_LINES - 1), %o1
+1: lda [%o0] ASI_DCACHE_FLUSH, %g0
+ cmp %o0, %o1
+ bl 1b
+ add %o0, DCACHE_INCR, %o0
+
+ or %o2, MMCR_DSET1, %o2 ! flush DSET 1
+ clr %o0
+ sta %o2, [%g0] ASI_MMCR
+1: lda [%o0] ASI_DCACHE_FLUSH, %g0
+ cmp %o0, %o1
+ bl 1b
+ add %o0, DCACHE_INCR, %o0
+
+ /* invalidate caches */
+ sta %g0, [%g0] ASI_DCACHE_INVAL
+ sta %g0, [%g0] ASI_ICACHE_INVAL
+
+ /* invalidate TLBs */
+ sta %g0, [%g0] ASI_PID
+ sta %g0, [%g0] ASI_PIID
+ sta %g0, [%g0] ASI_GTLB_INVAL_PID
+ nop; nop; nop
+
+ /* set up new page tables */
+ sta %o3, [%g0] ASI_PDBR
+ nop; nop; nop
+
+ sta %g0, [%g0] ASI_GTLB_INVALIDATE
+ nop
+
+ /*
+ * Before we restore the window from the stack, we have to make
+ * sure it is in the TLB...
+ */
+ mov %o6, %o3
+ PTE_OF_ADDR(%o3, %o1, %o2, badstack)
+ INSERT_PTE(%o3, %o1)
+
+ SLT_IF_1PAGE_RW(%o3, %o1, %o2)
+ bl 9f
+ add %o3, 7 * 8, %o3
+ PTE_OF_ADDR(%o3, %o1, %o2, badstack)
+ INSERT_PTE(%o3, %o1)
+ /* b 9f; nop */
+
+badstack:
+ /*
+ * If control goes there, the stack we are restoring does not have
+ * a valid mapping. We can't recover and will double fault very quickly.
+ */
+
+9:
+ /* load window */
+ ldd [%sp + (0*8)], %l0
+ ldd [%sp + (1*8)], %l2
+ ldd [%sp + (2*8)], %l4
+ ldd [%sp + (3*8)], %l6
+ ldd [%sp + (4*8)], %i0
+ ldd [%sp + (5*8)], %i2
+ ldd [%sp + (6*8)], %i4
+ ldd [%sp + (7*8)], %i6
+#ifdef DEBUG
+ mov %g5, %o0
+ SET_SP_REDZONE(%o0, %o1)
+ CHECK_SP_REDZONE(%o0, %o1)
+#endif
+ /* finally, enable traps */
+ wr %g2, 0, %psr ! psr = newpsr;
+
+ /*
+ * Now running p. Make sure it has a context so that it
+ * can talk about user space stuff. (Its pcb_uw is currently
+ * zero so it is safe to have interrupts going here.)
+ */
+
+ retl
+ EMPTY
+1:
+
+Lsw_sameproc:
+ /*
+ * We are resuming the process that was running at the
+ * call to switch(). Just set psr ipl and return.
+ */
+! wr %g2, 0 %psr ! %psr = newpsr; (done earlier)
+ nop
+ retl
+ nop
+
+
+/*
+ * Snapshot the current process so that stack frames are up to date.
+ * Only used just before a crash dump.
+ */
+ENTRY(snapshot)
+ std %o6, [%o0 + PCB_SP] ! save sp
+ rd %psr, %o1 ! save psr
+ st %o1, [%o0 + PCB_PSR]
+
+ /*
+ * Just like switch(); same XXX comments apply.
+ * 7 of each. Minor tweak: the 7th restore is
+ * done after a ret.
+ */
+ SAVE; SAVE; SAVE; SAVE; SAVE; SAVE; SAVE
+ restore; restore; restore; restore; restore; restore; ret; restore
+
+
+/*
+ * cpu_set_kpc() and cpu_fork() arrange for proc_trampoline() to run
+ * after after a process gets chosen in switch(). The stack frame will
+ * contain a function pointer in %l0, and an argument to pass to it in %l2.
+ *
+ * If the function *(%l0) returns, we arrange for an immediate return
+ * to user mode. This happens in two known cases: after execve(2) of init,
+ * and when returning a child to user mode after a fork(2).
+ */
+ENTRY(proc_trampoline)
+ /* Reset interrupt level */
+ rd %psr, %o0
+ andn %o0, PSR_PIL, %o0 ! psr &= ~PSR_PIL;
+ wr %o0, 0, %psr ! (void) spl0();
+ nop ! psr delay; the next 2 instructions
+ ! can safely be made part of the
+ ! required 3 instructions psr delay
+ call %l0 ! re-use current frame
+ mov %l1, %o0
+
+ /*
+ * Here we finish up as in syscall, but simplified. We need to
+ * fiddle pc and npc a bit, as execve() / setregs() /cpu_set_kpc()
+ * have only set npc, in anticipation that trap.c will advance past
+ * the trap instruction; but we bypass that, so we must do it manually.
+ */
+ mov PSR_S, %l0 ! user psr (no need to load it)
+ !?wr %g0, 2, %wim ! %wim = 2
+ ld [%sp + CCFSZ + 8], %l1 ! pc = tf->tf_npc from execve/fork
+ b return_from_syscall
+ add %l1, 4, %l2 ! npc = pc+4
+
+/* probeget and probeset are meant to be used during autoconfiguration */
+
+/*
+ * probeget(addr, size) caddr_t addr; int size;
+ *
+ * Read or write a (byte,word,longword) from the given address.
+ * Like {fu,su}{byte,halfword,word} but our caller is supposed
+ * to know what he is doing... the address can be anywhere.
+ *
+ * We optimize for space, rather than time, here.
+ */
+ENTRY(probeget)
+ ! %o0 = addr, %o1 = (1,2,4)
+ sethi %hi(_C_LABEL(cpcb)), %o2
+ ld [%o2 + %lo(_C_LABEL(cpcb))], %o2 ! cpcb->pcb_onfault = Lfserr;
+ set Lfserr, %o5
+ st %o5, [%o2 + PCB_ONFAULT]
+ btst 1, %o1
+ bnz,a 0f ! if (len & 1)
+ ldub [%o0], %o0 ! value = *(char *)addr;
+0: btst 2, %o1
+ bnz,a 0f ! if (len & 2)
+ lduh [%o0], %o0 ! value = *(short *)addr;
+0: btst 4, %o1
+ bnz,a 0f ! if (len & 4)
+ ld [%o0], %o0 ! value = *(int *)addr;
+0: retl ! made it, clear onfault and return
+ st %g0, [%o2 + PCB_ONFAULT]
+
+Lfserr:
+ st %g0, [%o2 + PCB_ONFAULT]! error in r/w, clear pcb_onfault
+ retl ! and return error indicator
+ mov -1, %o0
+
+ /*
+ * This is just like Lfserr, but it's a global label that allows
+ * mem_access_fault() to check to see that we don't want to try to
+ * page in the fault. It's used by xldcontrolb().
+ */
+ .globl _C_LABEL(Lfsbail)
+Lfsbail:
+ st %g0, [%o2 + PCB_ONFAULT]! error in r/w, clear pcb_onfault
+ retl ! and return error indicator
+ mov -1, %o0
+
+/*
+ * probeset(addr, size, val) caddr_t addr; int size, val;
+ *
+ * As above, but we return 0 on success.
+ */
+ENTRY(probeset)
+ ! %o0 = addr, %o1 = (1,2,4), %o2 = val
+ sethi %hi(_C_LABEL(cpcb)), %o3
+ ld [%o3 + %lo(_C_LABEL(cpcb))], %o3 ! cpcb->pcb_onfault = Lfserr;
+ set Lfserr, %o5
+ st %o5, [%o3 + PCB_ONFAULT]
+ btst 1, %o1
+ bnz,a 0f ! if (len & 1)
+ stb %o2, [%o0] ! *(char *)addr = value;
+0: btst 2, %o1
+ bnz,a 0f ! if (len & 2)
+ sth %o2, [%o0] ! *(short *)addr = value;
+0: btst 4, %o1
+ bnz,a 0f ! if (len & 4)
+ st %o2, [%o0] ! *(int *)addr = value;
+0: clr %o0 ! made it, clear onfault and return 0
+ retl
+ st %g0, [%o3 + PCB_ONFAULT]
+
+/*
+ * Insert entry into doubly-linked queue.
+ * We could just do this in C, but gcc does not do leaves well (yet).
+ */
+ENTRY(_insque)
+ ! %o0 = e = what to insert; %o1 = after = entry to insert after
+ st %o1, [%o0 + 4] ! e->prev = after;
+ ld [%o1], %o2 ! tmp = after->next;
+ st %o2, [%o0] ! e->next = tmp;
+ st %o0, [%o1] ! after->next = e;
+ retl
+ st %o0, [%o2 + 4] ! tmp->prev = e;
+
+
+/*
+ * Remove entry from doubly-linked queue.
+ */
+ENTRY(_remque)
+ ! %o0 = e = what to remove
+ ld [%o0], %o1 ! n = e->next;
+ ld [%o0 + 4], %o2 ! p = e->prev;
+ st %o2, [%o1 + 4] ! n->prev = p;
+ retl
+ st %o1, [%o2] ! p->next = n;
+
+/*
+ * copywords(src, dst, nbytes)
+ *
+ * Copy `nbytes' bytes from src to dst, both of which are word-aligned;
+ * nbytes is a multiple of four. It may, however, be zero, in which case
+ * nothing is to be copied.
+ */
+ENTRY(copywords)
+ ! %o0 = src, %o1 = dst, %o2 = nbytes
+ b 1f
+ deccc 4, %o2
+0:
+ st %o3, [%o1 + %o2]
+ deccc 4, %o2 ! while ((n -= 4) >= 0)
+1:
+ bge,a 0b ! *(int *)(dst+n) = *(int *)(src+n);
+ ld [%o0 + %o2], %o3
+ retl
+ nop
+
+/*
+ * qcopy(src, dst, nbytes)
+ *
+ * (q for `quad' or `quick', as opposed to b for byte/block copy)
+ *
+ * Just like copywords, but everything is multiples of 8.
+ */
+ENTRY(qcopy)
+ b 1f
+ deccc 8, %o2
+0:
+ std %o4, [%o1 + %o2]
+ deccc 8, %o2
+1:
+ bge,a 0b
+ ldd [%o0 + %o2], %o4
+ retl
+ nop
+
+/*
+ * qzero(addr, nbytes)
+ *
+ * Zeroes `nbytes' bytes of a quad-aligned virtual address,
+ * where nbytes is itself a multiple of 8.
+ */
+ENTRY(qzero)
+ ! %o0 = addr, %o1 = len (in bytes)
+ clr %g1
+0:
+ deccc 8, %o1 ! while ((n =- 8) >= 0)
+ bge,a 0b
+ std %g0, [%o0 + %o1] ! *(quad *)(addr + n) = 0;
+ retl
+ nop
+
+/*
+ * kernel bcopy/memcpy
+ * Assumes regions do not overlap; has no useful return value.
+ *
+ * Must not use %g7 (see copyin/copyout above).
+ */
+
+#define BCOPY_SMALL 32 /* if < 32, copy by bytes */
+
+ENTRY(memcpy)
+ /*
+ * Swap args for bcopy. Gcc generates calls to memcpy for
+ * structure assignments.
+ */
+ mov %o0, %o3
+ mov %o1, %o0
+ mov %o3, %o1
+ENTRY(bcopy)
+ cmp %o2, BCOPY_SMALL
+Lbcopy_start:
+ bge,a Lbcopy_fancy ! if >= this many, go be fancy.
+ btst 7, %o0 ! (part of being fancy)
+
+ /*
+ * Not much to copy, just do it a byte at a time.
+ */
+ deccc %o2 ! while (--len >= 0)
+ bl 1f
+ EMPTY
+0:
+ inc %o0
+ ldsb [%o0 - 1], %o4 ! (++dst)[-1] = *src++;
+ stb %o4, [%o1]
+ deccc %o2
+ bge 0b
+ inc %o1
+1:
+ retl
+ nop
+ /* NOTREACHED */
+
+ /*
+ * Plenty of data to copy, so try to do it optimally.
+ */
+Lbcopy_fancy:
+ ! check for common case first: everything lines up.
+! btst 7, %o0 ! done already
+ bne 1f
+ EMPTY
+ btst 7, %o1
+ be,a Lbcopy_doubles
+ dec 8, %o2 ! if all lined up, len -= 8, goto bcopy_doubes
+
+ ! If the low bits match, we can make these line up.
+1:
+ xor %o0, %o1, %o3 ! t = src ^ dst;
+ btst 1, %o3 ! if (t & 1) {
+ be,a 1f
+ btst 1, %o0 ! [delay slot: if (src & 1)]
+
+ ! low bits do not match, must copy by bytes.
+0:
+ ldsb [%o0], %o4 ! do {
+ inc %o0 ! (++dst)[-1] = *src++;
+ inc %o1
+ deccc %o2
+ bnz 0b ! } while (--len != 0);
+ stb %o4, [%o1 - 1]
+ retl
+ nop
+ /* NOTREACHED */
+
+ ! lowest bit matches, so we can copy by words, if nothing else
+1:
+ be,a 1f ! if (src & 1) {
+ btst 2, %o3 ! [delay slot: if (t & 2)]
+
+ ! although low bits match, both are 1: must copy 1 byte to align
+ ldsb [%o0], %o4 ! *dst++ = *src++;
+ stb %o4, [%o1]
+ inc %o0
+ inc %o1
+ dec %o2 ! len--;
+ btst 2, %o3 ! } [if (t & 2)]
+1:
+ be,a 1f ! if (t & 2) {
+ btst 2, %o0 ! [delay slot: if (src & 2)]
+ dec 2, %o2 ! len -= 2;
+0:
+ ldsh [%o0], %o4 ! do {
+ sth %o4, [%o1] ! *(short *)dst = *(short *)src;
+ inc 2, %o0 ! dst += 2, src += 2;
+ deccc 2, %o2 ! } while ((len -= 2) >= 0);
+ bge 0b
+ inc 2, %o1
+ b Lbcopy_mopb ! goto mop_up_byte;
+ btst 1, %o2 ! } [delay slot: if (len & 1)]
+ /* NOTREACHED */
+
+ ! low two bits match, so we can copy by longwords
+1:
+ be,a 1f ! if (src & 2) {
+ btst 4, %o3 ! [delay slot: if (t & 4)]
+
+ ! although low 2 bits match, they are 10: must copy one short to align
+ ldsh [%o0], %o4 ! (*short *)dst = *(short *)src;
+ sth %o4, [%o1]
+ inc 2, %o0 ! dst += 2;
+ inc 2, %o1 ! src += 2;
+ dec 2, %o2 ! len -= 2;
+ btst 4, %o3 ! } [if (t & 4)]
+1:
+ be,a 1f ! if (t & 4) {
+ btst 4, %o0 ! [delay slot: if (src & 4)]
+ dec 4, %o2 ! len -= 4;
+0:
+ ld [%o0], %o4 ! do {
+ st %o4, [%o1] ! *(int *)dst = *(int *)src;
+ inc 4, %o0 ! dst += 4, src += 4;
+ deccc 4, %o2 ! } while ((len -= 4) >= 0);
+ bge 0b
+ inc 4, %o1
+ b Lbcopy_mopw ! goto mop_up_word_and_byte;
+ btst 2, %o2 ! } [delay slot: if (len & 2)]
+ /* NOTREACHED */
+
+ ! low three bits match, so we can copy by doublewords
+1:
+ be 1f ! if (src & 4) {
+ dec 8, %o2 ! [delay slot: len -= 8]
+ ld [%o0], %o4 ! *(int *)dst = *(int *)src;
+ st %o4, [%o1]
+ inc 4, %o0 ! dst += 4, src += 4, len -= 4;
+ inc 4, %o1
+ dec 4, %o2 ! }
+1:
+Lbcopy_doubles:
+ ldd [%o0], %o4 ! do {
+ std %o4, [%o1] ! *(double *)dst = *(double *)src;
+ inc 8, %o0 ! dst += 8, src += 8;
+ deccc 8, %o2 ! } while ((len -= 8) >= 0);
+ bge Lbcopy_doubles
+ inc 8, %o1
+
+ ! check for a usual case again (save work)
+ btst 7, %o2 ! if ((len & 7) == 0)
+ be Lbcopy_done ! goto bcopy_done;
+
+ btst 4, %o2 ! if ((len & 4)) == 0)
+ be,a Lbcopy_mopw ! goto mop_up_word_and_byte;
+ btst 2, %o2 ! [delay slot: if (len & 2)]
+ ld [%o0], %o4 ! *(int *)dst = *(int *)src;
+ st %o4, [%o1]
+ inc 4, %o0 ! dst += 4;
+ inc 4, %o1 ! src += 4;
+ btst 2, %o2 ! } [if (len & 2)]
+
+1:
+ ! mop up trailing word (if present) and byte (if present).
+Lbcopy_mopw:
+ be Lbcopy_mopb ! no word, go mop up byte
+ btst 1, %o2 ! [delay slot: if (len & 1)]
+ ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
+ be Lbcopy_done ! if ((len & 1) == 0) goto done;
+ sth %o4, [%o1]
+ ldsb [%o0 + 2], %o4 ! dst[2] = src[2];
+ retl
+ stb %o4, [%o1 + 2]
+ /* NOTREACHED */
+
+ ! mop up trailing byte (if present).
+Lbcopy_mopb:
+ bne,a 1f
+ ldsb [%o0], %o4
+
+Lbcopy_done:
+ retl
+ nop
+
+1:
+ retl
+ stb %o4,[%o1]
+/*
+ * ovbcopy(src, dst, len): like bcopy, but regions may overlap.
+ */
+ENTRY(ovbcopy)
+ cmp %o0, %o1 ! src < dst?
+ bgeu Lbcopy_start ! no, go copy forwards as via bcopy
+ cmp %o2, BCOPY_SMALL! (check length for doublecopy first)
+
+ /*
+ * Since src comes before dst, and the regions might overlap,
+ * we have to do the copy starting at the end and working backwards.
+ */
+ add %o2, %o0, %o0 ! src += len
+ add %o2, %o1, %o1 ! dst += len
+ bge,a Lback_fancy ! if len >= BCOPY_SMALL, go be fancy
+ btst 3, %o0
+
+ /*
+ * Not much to copy, just do it a byte at a time.
+ */
+ deccc %o2 ! while (--len >= 0)
+ bl 1f
+ EMPTY
+0:
+ dec %o0 ! *--dst = *--src;
+ ldsb [%o0], %o4
+ dec %o1
+ deccc %o2
+ bge 0b
+ stb %o4, [%o1]
+1:
+ retl
+ nop
+
+ /*
+ * Plenty to copy, try to be optimal.
+ * We only bother with word/halfword/byte copies here.
+ */
+Lback_fancy:
+! btst 3, %o0 ! done already
+ bnz 1f ! if ((src & 3) == 0 &&
+ btst 3, %o1 ! (dst & 3) == 0)
+ bz,a Lback_words ! goto words;
+ dec 4, %o2 ! (done early for word copy)
+
+1:
+ /*
+ * See if the low bits match.
+ */
+ xor %o0, %o1, %o3 ! t = src ^ dst;
+ btst 1, %o3
+ bz,a 3f ! if (t & 1) == 0, can do better
+ btst 1, %o0
+
+ /*
+ * Nope; gotta do byte copy.
+ */
+2:
+ dec %o0 ! do {
+ ldsb [%o0], %o4 ! *--dst = *--src;
+ dec %o1
+ deccc %o2 ! } while (--len != 0);
+ bnz 2b
+ stb %o4, [%o1]
+ retl
+ nop
+
+3:
+ /*
+ * Can do halfword or word copy, but might have to copy 1 byte first.
+ */
+! btst 1, %o0 ! done earlier
+ bz,a 4f ! if (src & 1) { /* copy 1 byte */
+ btst 2, %o3 ! (done early)
+ dec %o0 ! *--dst = *--src;
+ ldsb [%o0], %o4
+ dec %o1
+ stb %o4, [%o1]
+ dec %o2 ! len--;
+ btst 2, %o3 ! }
+
+4:
+ /*
+ * See if we can do a word copy ((t&2) == 0).
+ */
+! btst 2, %o3 ! done earlier
+ bz,a 6f ! if (t & 2) == 0, can do word copy
+ btst 2, %o0 ! (src&2, done early)
+
+ /*
+ * Gotta do halfword copy.
+ */
+ dec 2, %o2 ! len -= 2;
+5:
+ dec 2, %o0 ! do {
+ ldsh [%o0], %o4 ! src -= 2;
+ dec 2, %o1 ! dst -= 2;
+ deccc 2, %o0 ! *(short *)dst = *(short *)src;
+ bge 5b ! } while ((len -= 2) >= 0);
+ sth %o4, [%o1]
+ b Lback_mopb ! goto mop_up_byte;
+ btst 1, %o2 ! (len&1, done early)
+
+6:
+ /*
+ * We can do word copies, but we might have to copy
+ * one halfword first.
+ */
+! btst 2, %o0 ! done already
+ bz 7f ! if (src & 2) {
+ dec 4, %o2 ! (len -= 4, done early)
+ dec 2, %o0 ! src -= 2, dst -= 2;
+ ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
+ dec 2, %o1
+ sth %o4, [%o1]
+ dec 2, %o2 ! len -= 2;
+ ! }
+
+7:
+Lback_words:
+ /*
+ * Do word copies (backwards), then mop up trailing halfword
+ * and byte if any.
+ */
+! dec 4, %o2 ! len -= 4, done already
+0: ! do {
+ dec 4, %o0 ! src -= 4;
+ dec 4, %o1 ! src -= 4;
+ ld [%o0], %o4 ! *(int *)dst = *(int *)src;
+ deccc 4, %o2 ! } while ((len -= 4) >= 0);
+ bge 0b
+ st %o4, [%o1]
+
+ /*
+ * Check for trailing shortword.
+ */
+ btst 2, %o2 ! if (len & 2) {
+ bz,a 1f
+ btst 1, %o2 ! (len&1, done early)
+ dec 2, %o0 ! src -= 2, dst -= 2;
+ ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
+ dec 2, %o1
+ sth %o4, [%o1] ! }
+ btst 1, %o2
+
+ /*
+ * Check for trailing byte.
+ */
+1:
+Lback_mopb:
+! btst 1, %o2 ! (done already)
+ bnz,a 1f ! if (len & 1) {
+ ldsb [%o0 - 1], %o4 ! b = src[-1];
+ retl
+ nop
+1:
+ retl ! dst[-1] = b;
+ stb %o4, [%o1 - 1] ! }
+
+/*
+ * kcopy() is exactly like bcopy except that it set pcb_onfault such that
+ * when a fault occurs, it is able to return -1 to indicate this to the
+ * caller.
+ */
+ENTRY(kcopy)
+ sethi %hi(_C_LABEL(cpcb)), %o5 ! cpcb->pcb_onfault = Lkcerr;
+ ld [%o5 + %lo(_C_LABEL(cpcb))], %o5
+ set Lkcerr, %o3
+ ld [%o5 + PCB_ONFAULT], %g1! save current onfault handler
+ st %o3, [%o5 + PCB_ONFAULT]
+
+ cmp %o2, BCOPY_SMALL
+Lkcopy_start:
+ bge,a Lkcopy_fancy ! if >= this many, go be fancy.
+ btst 7, %o0 ! (part of being fancy)
+
+ /*
+ * Not much to copy, just do it a byte at a time.
+ */
+ deccc %o2 ! while (--len >= 0)
+ bl 1f
+ EMPTY
+0:
+ ldsb [%o0], %o4 ! *dst++ = *src++;
+ inc %o0
+ stb %o4, [%o1]
+ deccc %o2
+ bge 0b
+ inc %o1
+1:
+ st %g1, [%o5 + PCB_ONFAULT] ! restore onfault
+ retl
+ mov 0, %o0 ! delay slot: return success
+ /* NOTREACHED */
+
+ /*
+ * Plenty of data to copy, so try to do it optimally.
+ */
+Lkcopy_fancy:
+ ! check for common case first: everything lines up.
+! btst 7, %o0 ! done already
+ bne 1f
+ EMPTY
+ btst 7, %o1
+ be,a Lkcopy_doubles
+ dec 8, %o2 ! if all lined up, len -= 8, goto bcopy_doubes
+
+ ! If the low bits match, we can make these line up.
+1:
+ xor %o0, %o1, %o3 ! t = src ^ dst;
+ btst 1, %o3 ! if (t & 1) {
+ be,a 1f
+ btst 1, %o0 ! [delay slot: if (src & 1)]
+
+ ! low bits do not match, must copy by bytes.
+0:
+ ldsb [%o0], %o4 ! do {
+ inc %o0 ! *dst++ = *src++;
+ stb %o4, [%o1]
+ deccc %o2
+ bnz 0b ! } while (--len != 0);
+ inc %o1
+ st %g1, [%o5 + PCB_ONFAULT] ! restore onfault
+ retl
+ mov 0, %o0 ! delay slot: return success
+ /* NOTREACHED */
+
+ ! lowest bit matches, so we can copy by words, if nothing else
+1:
+ be,a 1f ! if (src & 1) {
+ btst 2, %o3 ! [delay slot: if (t & 2)]
+
+ ! although low bits match, both are 1: must copy 1 byte to align
+ ldsb [%o0], %o4 ! *dst++ = *src++;
+ inc %o0
+ stb %o4, [%o1]
+ dec %o2 ! len--;
+ inc %o1
+ btst 2, %o3 ! } [if (t & 2)]
+1:
+ be,a 1f ! if (t & 2) {
+ btst 2, %o0 ! [delay slot: if (src & 2)]
+ dec 2, %o2 ! len -= 2;
+0:
+ ldsh [%o0], %o4 ! do {
+ inc 2, %o0 ! dst += 2, src += 2;
+ sth %o4, [%o1] ! *(short *)dst = *(short *)src;
+ deccc 2, %o2 ! } while ((len -= 2) >= 0);
+ bge 0b
+ inc 2, %o1
+ b Lkcopy_mopb ! goto mop_up_byte;
+ btst 1, %o2 ! } [delay slot: if (len & 1)]
+ /* NOTREACHED */
+
+ ! low two bits match, so we can copy by longwords
+1:
+ be,a 1f ! if (src & 2) {
+ btst 4, %o3 ! [delay slot: if (t & 4)]
+
+ ! although low 2 bits match, they are 10: must copy one short to align
+ ldsh [%o0], %o4 ! (*short *)dst = *(short *)src;
+ inc 2, %o0 ! dst += 2;
+ sth %o4, [%o1]
+ dec 2, %o2 ! len -= 2;
+ inc 2, %o1 ! src += 2;
+ btst 4, %o3 ! } [if (t & 4)]
+1:
+ be,a 1f ! if (t & 4) {
+ btst 4, %o0 ! [delay slot: if (src & 4)]
+ dec 4, %o2 ! len -= 4;
+0:
+ ld [%o0], %o4 ! do {
+ inc 4, %o0 ! dst += 4, src += 4;
+ st %o4, [%o1] ! *(int *)dst = *(int *)src;
+ deccc 4, %o2 ! } while ((len -= 4) >= 0);
+ bge 0b
+ inc 4, %o1
+ b Lkcopy_mopw ! goto mop_up_word_and_byte;
+ btst 2, %o2 ! } [delay slot: if (len & 2)]
+ /* NOTREACHED */
+
+ ! low three bits match, so we can copy by doublewords
+1:
+ be 1f ! if (src & 4) {
+ dec 8, %o2 ! [delay slot: len -= 8]
+ ld [%o0], %o4 ! *(int *)dst = *(int *)src;
+ inc 4, %o0 ! dst += 4, src += 4, len -= 4;
+ st %o4, [%o1]
+ dec 4, %o2 ! }
+ inc 4, %o1
+1:
+Lkcopy_doubles:
+ ! swap %o4 with %o2 during doubles copy, since %o5 is verboten
+ mov %o2, %o4
+Lkcopy_doubles2:
+ ldd [%o0], %o2 ! do {
+ inc 8, %o0 ! dst += 8, src += 8;
+ std %o2, [%o1] ! *(double *)dst = *(double *)src;
+ deccc 8, %o4 ! } while ((len -= 8) >= 0);
+ bge Lkcopy_doubles2
+ inc 8, %o1
+ mov %o4, %o2 ! restore len
+
+ ! check for a usual case again (save work)
+ btst 7, %o2 ! if ((len & 7) == 0)
+ be Lkcopy_done ! goto bcopy_done;
+
+ btst 4, %o2 ! if ((len & 4)) == 0)
+ be,a Lkcopy_mopw ! goto mop_up_word_and_byte;
+ btst 2, %o2 ! [delay slot: if (len & 2)]
+ ld [%o0], %o4 ! *(int *)dst = *(int *)src;
+ inc 4, %o0 ! dst += 4;
+ st %o4, [%o1]
+ inc 4, %o1 ! src += 4;
+ btst 2, %o2 ! } [if (len & 2)]
+
+1:
+ ! mop up trailing word (if present) and byte (if present).
+Lkcopy_mopw:
+ be Lkcopy_mopb ! no word, go mop up byte
+ btst 1, %o2 ! [delay slot: if (len & 1)]
+ ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
+ be Lkcopy_done ! if ((len & 1) == 0) goto done;
+ sth %o4, [%o1]
+ ldsb [%o0 + 2], %o4 ! dst[2] = src[2];
+ stb %o4, [%o1 + 2]
+ st %g1, [%o5 + PCB_ONFAULT]! restore onfault
+ retl
+ mov 0, %o0 ! delay slot: return success
+ /* NOTREACHED */
+
+ ! mop up trailing byte (if present).
+Lkcopy_mopb:
+ bne,a 1f
+ ldsb [%o0], %o4
+
+Lkcopy_done:
+ st %g1, [%o5 + PCB_ONFAULT] ! restore onfault
+ retl
+ mov 0, %o0 ! delay slot: return success
+ /* NOTREACHED */
+
+1:
+ stb %o4, [%o1]
+ st %g1, [%o5 + PCB_ONFAULT] ! restore onfault
+ retl
+ mov 0, %o0 ! delay slot: return success
+ /* NOTREACHED */
+
+Lkcerr:
+ st %g1, [%o5 + PCB_ONFAULT] ! restore onfault
+ retl
+ mov EFAULT, %o0 ! delay slot: return error indicator
+ /* NOTREACHED */
+
+/*
+ * savefpstate(f) struct fpstate *f;
+ *
+ * Store the current FPU state. The first `st %fsr' may cause a trap;
+ * our trap handler knows how to recover (by `returning' to savefpcont).
+ */
+ENTRY(savefpstate)
+ rd %psr, %o1 ! enable FP before we begin
+ set PSR_EF, %o2
+ or %o1, %o2, %o1
+ wr %o1, 0, %psr
+ /* do some setup work while we wait for PSR_EF to turn on */
+ set FSR_QNE, %o5 ! QNE = 0x2000, too big for immediate
+ clr %o3 ! qsize = 0;
+ nop ! (still waiting for PSR_EF)
+special_fp_store:
+ st %fsr, [%o0 + FS_FSR] ! f->fs_fsr = getfsr();
+ /*
+ * Even if the preceding instruction did not trap, the queue
+ * is not necessarily empty: this state save might be happening
+ * because user code tried to store %fsr and took the FPU
+ * from `exception pending' mode to `exception' mode.
+ * So we still have to check the blasted QNE bit.
+ * With any luck it will usually not be set.
+ */
+ ld [%o0 + FS_FSR], %o4 ! if (f->fs_fsr & QNE)
+ btst %o5, %o4
+ bnz Lfp_storeq ! goto storeq;
+ std %f0, [%o0 + FS_REGS + (4*0)] ! f->fs_f0 = etc;
+Lfp_finish:
+ st %o3, [%o0 + FS_QSIZE] ! f->fs_qsize = qsize;
+ std %f2, [%o0 + FS_REGS + (4*2)]
+ std %f4, [%o0 + FS_REGS + (4*4)]
+ std %f6, [%o0 + FS_REGS + (4*6)]
+ std %f8, [%o0 + FS_REGS + (4*8)]
+ std %f10, [%o0 + FS_REGS + (4*10)]
+ std %f12, [%o0 + FS_REGS + (4*12)]
+ std %f14, [%o0 + FS_REGS + (4*14)]
+ std %f16, [%o0 + FS_REGS + (4*16)]
+ std %f18, [%o0 + FS_REGS + (4*18)]
+ std %f20, [%o0 + FS_REGS + (4*20)]
+ std %f22, [%o0 + FS_REGS + (4*22)]
+ std %f24, [%o0 + FS_REGS + (4*24)]
+ std %f26, [%o0 + FS_REGS + (4*26)]
+ std %f28, [%o0 + FS_REGS + (4*28)]
+ retl
+ std %f30, [%o0 + FS_REGS + (4*30)]
+
+/*
+ * Store the (now known nonempty) FP queue.
+ * We have to reread the fsr each time in order to get the new QNE bit.
+ */
+Lfp_storeq:
+ add %o0, FS_QUEUE, %o1 ! q = &f->fs_queue[0];
+1:
+ std %fq, [%o1 + %o3] ! q[qsize++] = fsr_qfront();
+ st %fsr, [%o0 + FS_FSR] ! reread fsr
+ ld [%o0 + FS_FSR], %o4 ! if fsr & QNE, loop
+ btst %o5, %o4
+ bnz 1b
+ inc 8, %o3
+ b Lfp_finish ! set qsize and finish storing fregs
+ srl %o3, 3, %o3 ! (but first fix qsize)
+
+/*
+ * The fsr store trapped. Do it again; this time it will not trap.
+ * We could just have the trap handler return to the `st %fsr', but
+ * if for some reason it *does* trap, that would lock us into a tight
+ * loop. This way we panic instead. Whoopee.
+ */
+savefpcont:
+ b special_fp_store + 4 ! continue
+ st %fsr, [%o0 + FS_FSR] ! but first finish the %fsr store
+
+/*
+ * Load FPU state.
+ */
+ENTRY(loadfpstate)
+ rd %psr, %o1 ! enable FP before we begin
+ set PSR_EF, %o2
+ or %o1, %o2, %o1
+ wr %o1, 0, %psr
+ nop; nop; nop ! paranoia
+ ldd [%o0 + FS_REGS + (4*0)], %f0
+ ldd [%o0 + FS_REGS + (4*2)], %f2
+ ldd [%o0 + FS_REGS + (4*4)], %f4
+ ldd [%o0 + FS_REGS + (4*6)], %f6
+ ldd [%o0 + FS_REGS + (4*8)], %f8
+ ldd [%o0 + FS_REGS + (4*10)], %f10
+ ldd [%o0 + FS_REGS + (4*12)], %f12
+ ldd [%o0 + FS_REGS + (4*14)], %f14
+ ldd [%o0 + FS_REGS + (4*16)], %f16
+ ldd [%o0 + FS_REGS + (4*18)], %f18
+ ldd [%o0 + FS_REGS + (4*20)], %f20
+ ldd [%o0 + FS_REGS + (4*22)], %f22
+ ldd [%o0 + FS_REGS + (4*24)], %f24
+ ldd [%o0 + FS_REGS + (4*26)], %f26
+ ldd [%o0 + FS_REGS + (4*28)], %f28
+ ldd [%o0 + FS_REGS + (4*30)], %f30
+ retl
+ ld [%o0 + FS_FSR], %fsr ! setfsr(f->fs_fsr);
+
+/*
+ * ffs(), using table lookup.
+ * The process switch code shares the table, so we just put the
+ * whole thing here.
+ */
+ffstab:
+ .byte -24,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 00-0f */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 10-1f */
+ .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 20-2f */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 30-3f */
+ .byte 7,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 40-4f */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 50-5f */
+ .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 60-6f */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 70-7f */
+ .byte 8,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 80-8f */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 10-9f */
+ .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* a0-af */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* b0-bf */
+ .byte 7,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* c0-cf */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* d0-df */
+ .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* e0-ef */
+ .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* f0-ff */
+
+/*
+ * We use a table lookup on each byte.
+ *
+ * In each section below, %o1 is the current byte (0, 1, 2, or 3).
+ * The last byte is handled specially: for the first three,
+ * if that byte is nonzero, we return the table value
+ * (plus 0, 8, or 16 for the byte number), but for the last
+ * one, we just return the table value plus 24. This means
+ * that ffstab[0] must be -24 so that ffs(0) will return 0.
+ */
+ENTRY(ffs)
+ set ffstab, %o2
+ andcc %o0, 0xff, %o1 ! get low byte
+ bz,a 1f ! try again if 0
+ srl %o0, 8, %o0 ! delay slot, get ready for next byte
+
+ retl ! return ffstab[%o1]
+ ldsb [%o2 + %o1], %o0
+
+1:
+ andcc %o0, 0xff, %o1 ! byte 1 like byte 0...
+ bz,a 2f
+ srl %o0, 8, %o0 ! (use delay to prepare for byte 2)
+
+ ldsb [%o2 + %o1], %o0
+ retl ! return ffstab[%o1] + 8
+ add %o0, 8, %o0
+
+2:
+ andcc %o0, 0xff, %o1
+ bz,a 3f
+ srl %o0, 8, %o0 ! (prepare for byte 3)
+
+ ldsb [%o2 + %o1], %o0
+ retl ! return ffstab[%o1] + 16
+ add %o0, 16, %o0
+
+3: ! just return ffstab[%o0] + 24
+ ldsb [%o2 + %o0], %o0
+ retl
+ add %o0, 24, %o0
+
+/*
+ * V8 sparc .{,u}{mul,div,rem} replacements.
+ * We try to mimic them 100%. Full 64 bit sources or outputs, and
+ * these routines are required to update the condition codes.
+ */
+.globl _C_LABEL(_mulreplace), _C_LABEL(_mulreplace_end)
+_C_LABEL(_mulreplace):
+ smulcc %o0, %o1, %o0
+ retl
+ rd %y, %o1
+_C_LABEL(_mulreplace_end):
+
+.globl _C_LABEL(_umulreplace), _C_LABEL(_umulreplace_end)
+_C_LABEL(_umulreplace):
+ umulcc %o0, %o1, %o0
+ retl
+ rd %y, %o1
+_C_LABEL(_umulreplace_end):
+
+.globl _C_LABEL(_divreplace), _C_LABEL(_divreplace_end)
+_C_LABEL(_divreplace):
+ sra %o0, 31, %g1
+ wr %g1, 0, %y
+ nop
+ nop
+ nop
+ retl
+ sdivcc %o0, %o1, %o0
+_C_LABEL(_divreplace_end):
+
+.globl _C_LABEL(_udivreplace), _C_LABEL(_udivreplace_end)
+_C_LABEL(_udivreplace):
+ wr %g0, 0, %y
+ nop
+ nop
+ nop
+ retl
+ udivcc %o0, %o1, %o0
+_C_LABEL(_udivreplace_end):
+
+.globl _C_LABEL(_remreplace), _C_LABEL(_remreplace_end)
+_C_LABEL(_remreplace):
+ sra %o0, 31, %g1
+ wr %g1, 0, %y
+ nop
+ nop
+ nop
+ sdiv %o0, %o1, %o2
+ smul %o1, %o2, %o2
+ retl
+ subcc %o0, %o2, %o0
+_C_LABEL(_remreplace_end):
+
+.globl _C_LABEL(_uremreplace), _C_LABEL(_uremreplace_end)
+_C_LABEL(_uremreplace):
+ wr %g0, 0, %y
+ nop
+ nop
+ nop
+ udiv %o0, %o1, %o2
+ umul %o1, %o2, %o2
+ retl
+ subcc %o0, %o2, %o0
+_C_LABEL(_uremreplace_end):
+
+/*
+ * Signed multiply, from Appendix E of the Sparc Version 8
+ * Architecture Manual.
+ *
+ * Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the upper 32 bits of
+ * the 64-bit product).
+ *
+ * This code optimizes short (less than 13-bit) multiplies.
+ */
+.globl .mul, _C_LABEL(_mul)
+.mul:
+_C_LABEL(_mul):
+ mov %o0, %y ! multiplier -> Y
+ andncc %o0, 0xfff, %g0 ! test bits 12..31
+ be Lmul_shortway ! if zero, can do it the short way
+ andcc %g0, %g0, %o4 ! zero the partial product and clear N and V
+
+ /*
+ * Long multiply. 32 steps, followed by a final shift step.
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %o1, %o4 ! 13
+ mulscc %o4, %o1, %o4 ! 14
+ mulscc %o4, %o1, %o4 ! 15
+ mulscc %o4, %o1, %o4 ! 16
+ mulscc %o4, %o1, %o4 ! 17
+ mulscc %o4, %o1, %o4 ! 18
+ mulscc %o4, %o1, %o4 ! 19
+ mulscc %o4, %o1, %o4 ! 20
+ mulscc %o4, %o1, %o4 ! 21
+ mulscc %o4, %o1, %o4 ! 22
+ mulscc %o4, %o1, %o4 ! 23
+ mulscc %o4, %o1, %o4 ! 24
+ mulscc %o4, %o1, %o4 ! 25
+ mulscc %o4, %o1, %o4 ! 26
+ mulscc %o4, %o1, %o4 ! 27
+ mulscc %o4, %o1, %o4 ! 28
+ mulscc %o4, %o1, %o4 ! 29
+ mulscc %o4, %o1, %o4 ! 30
+ mulscc %o4, %o1, %o4 ! 31
+ mulscc %o4, %o1, %o4 ! 32
+ mulscc %o4, %g0, %o4 ! final shift
+
+ ! If %o0 was negative, the result is
+ ! (%o0 * %o1) + (%o1 << 32))
+ ! We fix that here.
+
+ tst %o0
+ bge 1f
+ rd %y, %o0
+
+ ! %o0 was indeed negative; fix upper 32 bits of result by subtracting
+ ! %o1 (i.e., return %o4 - %o1 in %o1).
+ retl
+ sub %o4, %o1, %o1
+
+1:
+ retl
+ mov %o4, %o1
+
+Lmul_shortway:
+ /*
+ * Short multiply. 12 steps, followed by a final shift step.
+ * The resulting bits are off by 12 and (32-12) = 20 bit positions,
+ * but there is no problem with %o0 being negative (unlike above).
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %g0, %o4 ! final shift
+
+ /*
+ * %o4 has 20 of the bits that should be in the low part of the
+ * result; %y has the bottom 12 (as %y's top 12). That is:
+ *
+ * %o4 %y
+ * +----------------+----------------+
+ * | -12- | -20- | -12- | -20- |
+ * +------(---------+------)---------+
+ * --hi-- ----low-part----
+ *
+ * The upper 12 bits of %o4 should be sign-extended to form the
+ * high part of the product (i.e., highpart = %o4 >> 20).
+ */
+
+ rd %y, %o5
+ sll %o4, 12, %o0 ! shift middle bits left 12
+ srl %o5, 20, %o5 ! shift low bits right 20, zero fill at left
+ or %o5, %o0, %o0 ! construct low part of result
+ retl
+ sra %o4, 20, %o1 ! ... and extract high part of result
+
+/*
+ * Unsigned multiply. Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the
+ * upper 32 bits of the 64-bit product).
+ *
+ * This code optimizes short (less than 13-bit) multiplies. Short
+ * multiplies require 25 instruction cycles, and long ones require
+ * 45 instruction cycles.
+ *
+ * On return, overflow has occurred (%o1 is not zero) if and only if
+ * the Z condition code is clear, allowing, e.g., the following:
+ *
+ * call .umul
+ * nop
+ * bnz overflow (or tnz)
+ */
+.globl .umul, _C_LABEL(_umul)
+.umul:
+_C_LABEL(_umul):
+ or %o0, %o1, %o4
+ mov %o0, %y ! multiplier -> Y
+ andncc %o4, 0xfff, %g0 ! test bits 12..31 of *both* args
+ be Lumul_shortway ! if zero, can do it the short way
+ andcc %g0, %g0, %o4 ! zero the partial product and clear N and V
+
+ /*
+ * Long multiply. 32 steps, followed by a final shift step.
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %o1, %o4 ! 13
+ mulscc %o4, %o1, %o4 ! 14
+ mulscc %o4, %o1, %o4 ! 15
+ mulscc %o4, %o1, %o4 ! 16
+ mulscc %o4, %o1, %o4 ! 17
+ mulscc %o4, %o1, %o4 ! 18
+ mulscc %o4, %o1, %o4 ! 19
+ mulscc %o4, %o1, %o4 ! 20
+ mulscc %o4, %o1, %o4 ! 21
+ mulscc %o4, %o1, %o4 ! 22
+ mulscc %o4, %o1, %o4 ! 23
+ mulscc %o4, %o1, %o4 ! 24
+ mulscc %o4, %o1, %o4 ! 25
+ mulscc %o4, %o1, %o4 ! 26
+ mulscc %o4, %o1, %o4 ! 27
+ mulscc %o4, %o1, %o4 ! 28
+ mulscc %o4, %o1, %o4 ! 29
+ mulscc %o4, %o1, %o4 ! 30
+ mulscc %o4, %o1, %o4 ! 31
+ mulscc %o4, %o1, %o4 ! 32
+ mulscc %o4, %g0, %o4 ! final shift
+
+
+ /*
+ * Normally, with the shift-and-add approach, if both numbers are
+ * positive you get the correct result. WIth 32-bit two's-complement
+ * numbers, -x is represented as
+ *
+ * x 32
+ * ( 2 - ------ ) mod 2 * 2
+ * 32
+ * 2
+ *
+ * (the `mod 2' subtracts 1 from 1.bbbb). To avoid lots of 2^32s,
+ * we can treat this as if the radix point were just to the left
+ * of the sign bit (multiply by 2^32), and get
+ *
+ * -x = (2 - x) mod 2
+ *
+ * Then, ignoring the `mod 2's for convenience:
+ *
+ * x * y = xy
+ * -x * y = 2y - xy
+ * x * -y = 2x - xy
+ * -x * -y = 4 - 2x - 2y + xy
+ *
+ * For signed multiplies, we subtract (x << 32) from the partial
+ * product to fix this problem for negative multipliers (see mul.s).
+ * Because of the way the shift into the partial product is calculated
+ * (N xor V), this term is automatically removed for the multiplicand,
+ * so we don't have to adjust.
+ *
+ * But for unsigned multiplies, the high order bit wasn't a sign bit,
+ * and the correction is wrong. So for unsigned multiplies where the
+ * high order bit is one, we end up with xy - (y << 32). To fix it
+ * we add y << 32.
+ */
+ tst %o1
+ bl,a 1f ! if %o1 < 0 (high order bit = 1),
+ add %o4, %o0, %o4 ! %o4 += %o0 (add y to upper half)
+1: rd %y, %o0 ! get lower half of product
+ retl
+ addcc %o4, %g0, %o1 ! put upper half in place and set Z for %o1==0
+
+Lumul_shortway:
+ /*
+ * Short multiply. 12 steps, followed by a final shift step.
+ * The resulting bits are off by 12 and (32-12) = 20 bit positions,
+ * but there is no problem with %o0 being negative (unlike above),
+ * and overflow is impossible (the answer is at most 24 bits long).
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %g0, %o4 ! final shift
+
+ /*
+ * %o4 has 20 of the bits that should be in the result; %y has
+ * the bottom 12 (as %y's top 12). That is:
+ *
+ * %o4 %y
+ * +----------------+----------------+
+ * | -12- | -20- | -12- | -20- |
+ * +------(---------+------)---------+
+ * -----result-----
+ *
+ * The 12 bits of %o4 left of the `result' area are all zero;
+ * in fact, all top 20 bits of %o4 are zero.
+ */
+
+ rd %y, %o5
+ sll %o4, 12, %o0 ! shift middle bits left 12
+ srl %o5, 20, %o5 ! shift low bits right 20
+ or %o5, %o0, %o0
+ retl
+ addcc %g0, %g0, %o1 ! %o1 = zero, and set Z
+
+/*
+ * Here is a very good random number generator. This implementation is
+ * based on ``Two Fast Implementations of the "Minimal Standard" Random
+ * Number Generator", David G. Carta, Communications of the ACM, Jan 1990,
+ * Vol 33 No 1.
+ */
+ .data
+ .globl _C_LABEL(_randseed)
+_C_LABEL(_randseed):
+ .word 1
+ .text
+ENTRY(random)
+ sethi %hi(16807), %o1
+ wr %o1, %lo(16807), %y
+ sethi %hi(_C_LABEL(_randseed)), %g1
+ ld [%g1 + %lo(_C_LABEL(_randseed))], %o0
+ andcc %g0, 0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %o0, %o2
+ mulscc %o2, %g0, %o2
+ rd %y, %o3
+ srl %o2, 16, %o1
+ set 0xffff, %o4
+ and %o4, %o2, %o0
+ sll %o0, 15, %o0
+ srl %o3, 17, %o3
+ or %o3, %o0, %o0
+ addcc %o0, %o1, %o0
+ bneg 1f
+ sethi %hi(0x7fffffff), %o1
+ retl
+ st %o0, [%g1 + %lo(_C_LABEL(_randseed))]
+1:
+ or %o1, %lo(0x7fffffff), %o1
+ add %o0, 1, %o0
+ and %o1, %o0, %o0
+ retl
+ st %o0, [%g1 + %lo(_C_LABEL(_randseed))]
+
+/*
+ * delay function
+ *
+ * void delay(N) -- delay N microseconds
+ *
+ * Register usage: %o0 = "N" number of usecs to go (counts down to zero)
+ * %o1 = "timerblurb" (stays constant)
+ * %o2 = counter for 1 usec (counts down from %o1 to zero)
+ *
+ */
+
+ENTRY(delay) ! %o0 = n
+ subcc %o0, %g0, %g0
+ be 2f
+
+ sethi %hi(_C_LABEL(timerblurb)), %o1
+ ld [%o1 + %lo(_C_LABEL(timerblurb))], %o1 ! %o1 = timerblurb
+
+ addcc %o1, %g0, %o2 ! %o2 = cntr (start @ %o1), clear CCs
+ ! first time through only
+
+ ! delay 1 usec
+1: bne 1b ! come back here if not done
+ subcc %o2, 1, %o2 ! %o2 = %o2 - 1 [delay slot]
+
+ subcc %o0, 1, %o0 ! %o0 = %o0 - 1
+ bne 1b ! done yet?
+ addcc %o1, %g0, %o2 ! reinit %o2 and CCs [delay slot]
+ ! harmless if not branching
+2:
+ retl ! return
+ nop ! [delay slot]
+
+#if defined(KGDB) || defined(DDB) || defined(DIAGNOSTIC)
+/*
+ * Write all windows (user or otherwise), except the current one.
+ *
+ * THIS COULD BE DONE IN USER CODE
+ */
+ENTRY(write_all_windows)
+ /*
+ * g2 = g1 = nwindows - 1;
+ * while (--g1 > 0) save();
+ * while (--g2 > 0) restore();
+ */
+ sethi %hi(_C_LABEL(nwindows)), %g1
+ ld [%g1 + %lo(_C_LABEL(nwindows))], %g1
+ dec %g1
+ mov %g1, %g2
+
+1: deccc %g1
+ bg,a 1b
+ save %sp, -64, %sp
+
+2: deccc %g2
+ bg,a 2b
+ restore
+
+ retl
+ nop
+#endif /* KGDB */
+
+ENTRY(setjmp)
+ std %sp, [%o0+0] ! stack pointer & return pc
+ st %fp, [%o0+8] ! frame pointer
+ retl
+ clr %o0
+
+Lpanic_ljmp:
+ .asciz "longjmp botch"
+ _ALIGN
+
+ENTRY(longjmp)
+ addcc %o1, %g0, %g6 ! compute v ? v : 1 in a global register
+ be,a 0f
+ mov 1, %g6
+0:
+ mov %o0, %g1 ! save a in another global register
+ ld [%g1+8], %g7 /* get caller's frame */
+1:
+ cmp %fp, %g7 ! compare against desired frame
+ bl,a 1b ! if below,
+ restore ! pop frame and loop
+ be,a 2f ! if there,
+ ldd [%g1+0], %o2 ! fetch return %sp and pc, and get out
+
+Llongjmpbotch:
+ ! otherwise, went too far; bomb out
+ save %sp, -CCFSZ, %sp /* preserve current window */
+ sethi %hi(Lpanic_ljmp), %o0
+ call _C_LABEL(panic)
+ or %o0, %lo(Lpanic_ljmp), %o0;
+ unimp 0
+
+2:
+ cmp %o2, %sp ! %sp must not decrease
+ bge,a 3f
+ mov %o2, %sp ! it is OK, put it in place
+ b,a Llongjmpbotch
+3:
+ jmp %o3 + 8 ! success, return %g6
+ mov %g6, %o0
+
+/*
+ * Early console code. Ugly, but works.
+ */
+ENTRY(earlycnputc)
+ save %sp, -64, %sp
+ sethi %hi(ZS0_BASE), %l1
+1: ldub [%l1 + %lo(ZS0_BASE) + 0x10], %l2
+ btst ZSRR0_TX_READY, %l2
+ be 1b
+ nop
+ stb %i1, [%l1 + %lo(ZS0_BASE) + 0x18]
+ set 0x2000, %l1
+1: cmp %l1, 0
+ bne 1b
+ dec %l1
+ ret
+ restore
+
+ENTRY(earlycngetc)
+ save %sp, -64, %sp
+ sethi %hi(ZS0_BASE), %l1
+1: ldub [%l1 + %lo(ZS0_BASE) + 0x10], %l2
+ btst ZSRR0_RX_READY, %l2
+ be 1b
+ nop
+ ldub [%l1 + %lo(ZS0_BASE) + 0x18], %i0
+ set 0x2000, %l1
+1: cmp %l1, 0
+ bne 1b
+ dec %l1
+ ret
+ restore
+
+ .data
+#if defined(DDB) || NKSYMS > 0
+ .globl _C_LABEL(esym)
+_C_LABEL(esym):
+ .word 0
+#endif
+ .globl _C_LABEL(cold)
+_C_LABEL(cold):
+ .word 1 ! cold start flag
+
+ .globl _C_LABEL(proc0paddr)
+_C_LABEL(proc0paddr):
+ .word _C_LABEL(u0) ! KVA of proc0 uarea
+
+! StackGhost: added 2 symbols to ease debugging
+ .globl slowtrap
+ .globl winuf_invalid
+
+ .comm _C_LABEL(nwindows), 4
+ .comm _C_LABEL(curproc), 4
+ .comm _C_LABEL(qs), 32 * 8
+ .comm _C_LABEL(whichqs), 4