summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/i86pc')
-rw-r--r--usr/src/uts/i86pc/Makefile.files8
-rw-r--r--usr/src/uts/i86pc/Makefile.i86pc.shared11
-rw-r--r--usr/src/uts/i86pc/Makefile.rules13
-rw-r--r--usr/src/uts/i86pc/acpippm/Makefile91
-rw-r--r--usr/src/uts/i86pc/cpr/Makefile98
-rw-r--r--usr/src/uts/i86pc/genassym/Makefile2
-rw-r--r--usr/src/uts/i86pc/gfx_private/Makefile4
-rw-r--r--usr/src/uts/i86pc/io/acpippm.conf28
-rw-r--r--usr/src/uts/i86pc/io/cbe.c50
-rw-r--r--usr/src/uts/i86pc/io/consplat.c3
-rw-r--r--usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c320
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_drv.c20
-rw-r--r--usr/src/uts/i86pc/io/mp_platform_common.c182
-rw-r--r--usr/src/uts/i86pc/io/pci/pci.c68
-rw-r--r--usr/src/uts/i86pc/io/pciex/npe.c61
-rw-r--r--usr/src/uts/i86pc/io/pcplusmp/apic.c11
-rw-r--r--usr/src/uts/i86pc/io/ppm.conf46
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpippm.c443
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpippm.h41
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpisleep.c214
-rw-r--r--usr/src/uts/i86pc/io/psm/psm_common.c38
-rw-r--r--usr/src/uts/i86pc/io/psm/uppc.c2
-rw-r--r--usr/src/uts/i86pc/io/todpc_subr.c229
-rw-r--r--usr/src/uts/i86pc/ml/cpr_wakecode.s1033
-rw-r--r--usr/src/uts/i86pc/ml/offsets.in50
-rw-r--r--usr/src/uts/i86pc/os/acpi_stubs.c52
-rw-r--r--usr/src/uts/i86pc/os/cpr_impl.c1096
-rw-r--r--usr/src/uts/i86pc/os/ddi_impl.c18
-rw-r--r--usr/src/uts/i86pc/os/memscrub.c26
-rw-r--r--usr/src/uts/i86pc/os/mp_implfuncs.c2
-rw-r--r--usr/src/uts/i86pc/os/mp_machdep.c19
-rw-r--r--usr/src/uts/i86pc/os/mp_pc.c18
-rw-r--r--usr/src/uts/i86pc/os/mp_startup.c2
-rw-r--r--usr/src/uts/i86pc/os/timestamp.c170
-rw-r--r--usr/src/uts/i86pc/sys/apic.h1
-rw-r--r--usr/src/uts/i86pc/sys/cpr_impl.h72
-rw-r--r--usr/src/uts/i86pc/sys/cpr_wakecode.h153
-rw-r--r--usr/src/uts/i86pc/sys/machclock.h18
-rw-r--r--usr/src/uts/i86pc/sys/machsystm.h2
-rw-r--r--usr/src/uts/i86pc/sys/psm_common.h2
-rw-r--r--usr/src/uts/i86pc/sys/psm_types.h47
-rw-r--r--usr/src/uts/i86pc/sys/smp_impldefs.h2
42 files changed, 4591 insertions, 175 deletions
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index 270dcdfd7b..48e880943a 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -34,6 +34,7 @@
# object lists
#
CORE_OBJS += \
+ acpi_stubs.o \
biosdisk.o \
bios_call.o \
cbe.o \
@@ -179,6 +180,8 @@ MCAMD_OBJS += \
CPUDRV_OBJS += cpudrv.o cpudrv_plat.o cpu_acpi.o speedstep.o
PPM_OBJS += ppm_subr.o ppm.o ppm_plat.o
+ACPIPPM_OBJS += acpippm.o acpisleep.o
+
ROOTNEX_OBJS += rootnex.o
TZMON_OBJS += tzmon.o
UPPC_OBJS += uppc.o psm_common.o
@@ -213,7 +216,10 @@ ASSYM_DEPS += \
sseblk.o \
swtch.o \
syscall_asm.o \
- syscall_asm_amd64.o
+ syscall_asm_amd64.o \
+ cpr_wakecode.o
+
+CPR_IMPL_OBJS = cpr_impl.o cpr_wakecode.o
$(KDI_ASSYM_DEPS:%=$(OBJS_DIR)/%): $(DSF_DIR)/$(OBJS_DIR)/kdi_assym.h
diff --git a/usr/src/uts/i86pc/Makefile.i86pc.shared b/usr/src/uts/i86pc/Makefile.i86pc.shared
index 145001e1da..311e8ee50b 100644
--- a/usr/src/uts/i86pc/Makefile.i86pc.shared
+++ b/usr/src/uts/i86pc/Makefile.i86pc.shared
@@ -255,7 +255,11 @@ DRV_KMODS += tzmon
DRV_KMODS += battery
DRV_KMODS += cpudrv
-DRV_KMODS += ppm
+
+#
+# Platform Power Modules
+#
+DRV_KMODS += ppm acpippm
$(CLOSED_BUILD)CLOSED_DRV_KMODS += memtest
@@ -307,3 +311,8 @@ DACF_KMODS += consconfig_dacf
# 'Mach' Modules (/kernel/mach):
#
MACH_KMODS += uppc
+
+#
+# CPR Misc Module.
+#
+MISC_KMODS += cpr
diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules
index 07ee7dfe6c..8ca64e2fcb 100644
--- a/usr/src/uts/i86pc/Makefile.rules
+++ b/usr/src/uts/i86pc/Makefile.rules
@@ -92,6 +92,13 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pcplusmp/%.c
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pcplusmp/%.s
$(COMPILE.s) -o $@ $<
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/ppm/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/ppm/%.s
+ $(COMPILE.s) -o $@ $<
+
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/psm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -264,6 +271,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pcplusmp/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pcplusmp/%.s
@($(LHEAD) $(LINT.s) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/ppm/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/ppm/%.s
+ @($(LHEAD) $(LINT.s) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/psm/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/i86pc/acpippm/Makefile b/usr/src/uts/i86pc/acpippm/Makefile
new file mode 100644
index 0000000000..2731469730
--- /dev/null
+++ b/usr/src/uts/i86pc/acpippm/Makefile
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/i86pc/acpippm/Makefile
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the power managment
+# module for the ACPI subsystem
+#
+# i86pc implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = acpippm
+OBJECTS = $(ACPIPPM_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(ACPIPPM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/i86pc/io
+INC_PATH += -I$(UTSBASE)/i86pc/sys/acpi
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS) $(CONF_INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/cpr/Makefile b/usr/src/uts/i86pc/cpr/Makefile
new file mode 100644
index 0000000000..115b4d0800
--- /dev/null
+++ b/usr/src/uts/i86pc/cpr/Makefile
@@ -0,0 +1,98 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/i86pc/cpr/Makefile
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the cpr misc kernel module.
+#
+# i86pc implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = cpr
+#
+#
+OBJECTS = $(CPR_IMPL_OBJS:%=$(OBJS_DIR)/%) \
+ $(CPR_OBJS:%=$(OBJS_DIR)/%) \
+ $(CPR_INTEL_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(CPR_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(CPR_IMPL_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(CPR_INTEL_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Override defaults
+#
+LDFLAGS += -dy -N misc/acpica
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/genassym/Makefile b/usr/src/uts/i86pc/genassym/Makefile
index 55425ce673..4d164a896d 100644
--- a/usr/src/uts/i86pc/genassym/Makefile
+++ b/usr/src/uts/i86pc/genassym/Makefile
@@ -78,6 +78,8 @@ clean.lint:
install: def
+CPPFLAGS += -I../../i86pc/io/ppm
+
#
# Create assym.h
#
diff --git a/usr/src/uts/i86pc/gfx_private/Makefile b/usr/src/uts/i86pc/gfx_private/Makefile
index 86381e7a48..e3b23a0f57 100644
--- a/usr/src/uts/i86pc/gfx_private/Makefile
+++ b/usr/src/uts/i86pc/gfx_private/Makefile
@@ -20,7 +20,7 @@
#
#
# uts/i86pc/gfx_private/Makefile
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -45,6 +45,8 @@ LINTS = $(GFX_PRIVATE_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
VGATEXT_FONT = 8859-1
VGATEXT_SRC = $(UTSBASE)/intel/io/vgatext
+GFXP_VGATEXT = gfxp_vgatext
+GFXP_VGATEXT_SRC = $(UTSBASE)/i86pc/io/gfx_private
#
# dependency
diff --git a/usr/src/uts/i86pc/io/acpippm.conf b/usr/src/uts/i86pc/io/acpippm.conf
new file mode 100644
index 0000000000..53112bd96d
--- /dev/null
+++ b/usr/src/uts/i86pc/io/acpippm.conf
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+name="acpippm" parent="pseudo" instance=0;
diff --git a/usr/src/uts/i86pc/io/cbe.c b/usr/src/uts/i86pc/io/cbe.c
index 7667bb2219..6bf01fa283 100644
--- a/usr/src/uts/i86pc/io/cbe.c
+++ b/usr/src/uts/i86pc/io/cbe.c
@@ -38,6 +38,7 @@
#include <sys/psm.h>
#include <sys/atomic.h>
#include <sys/clock.h>
+#include <sys/x86_archext.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_intr.h>
#include <sys/avintr.h>
@@ -58,6 +59,8 @@ static ddi_softint_hdl_impl_t cbe_clock_hdl =
cyclic_id_t cbe_hres_cyclic;
int cbe_psm_timer_mode = TIMER_ONESHOT;
+extern int tsc_gethrtime_enable;
+
void cbe_hres_tick(void);
int
@@ -200,6 +203,43 @@ cbe_configure(cpu_t *cpu)
return (cpu);
}
+#ifndef __xpv
+/*
+ * declarations needed for time adjustment
+ */
+extern void tsc_suspend(void);
+extern void tsc_resume(void);
+/*
+ * Call the resume function in the cyclic, instead of inline in the
+ * resume path.
+ */
+extern int tsc_resume_in_cyclic;
+#endif
+
+/*ARGSUSED*/
+static void
+cbe_suspend(cyb_arg_t arg)
+{
+#ifndef __xpv
+ /*
+ * This is an x86 backend, so let the tsc_suspend
+ * that is specific to x86 platforms do the work.
+ */
+ tsc_suspend();
+#endif
+}
+
+/*ARGSUSED*/
+static void
+cbe_resume(cyb_arg_t arg)
+{
+#ifndef __xpv
+ if (tsc_resume_in_cyclic) {
+ tsc_resume();
+ }
+#endif
+}
+
void
cbe_enable(void *arg)
{
@@ -209,7 +249,11 @@ cbe_enable(void *arg)
if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0))
return;
- ASSERT(!CPU_IN_SET(cbe_enabled, me));
+ /*
+ * Added (me == 0) to the ASSERT because the timer isn't
+ * disabled on CPU 0, and cbe_enable is called when we resume.
+ */
+ ASSERT((me == 0) || !CPU_IN_SET(cbe_enabled, me));
CPUSET_ADD(cbe_enabled, me);
if (cbe_psm_timer_mode == TIMER_ONESHOT)
(*psm_timer_enable)();
@@ -268,8 +312,8 @@ cbe_init(void)
cbe_set_level, /* cyb_set_level */
cbe_restore_level, /* cyb_restore_level */
cbe_xcall, /* cyb_xcall */
- NULL, /* cyb_suspend */
- NULL /* cyb_resume */
+ cbe_suspend, /* cyb_suspend */
+ cbe_resume /* cyb_resume */
};
hrtime_t resolution;
cyc_handler_t hdlr;
diff --git a/usr/src/uts/i86pc/io/consplat.c b/usr/src/uts/i86pc/io/consplat.c
index cfb0f5e861..feecc2d3d5 100644
--- a/usr/src/uts/i86pc/io/consplat.c
+++ b/usr/src/uts/i86pc/io/consplat.c
@@ -54,7 +54,8 @@ static char *
gfxdrv_name[] = {
"vgatext",
"i915",
- "nvidia"
+ "atiatom",
+ "nvidia",
};
int
diff --git a/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c b/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c
index 543349655c..a9d3c3827e 100644
--- a/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c
+++ b/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c
@@ -49,6 +49,9 @@
#include <sys/kd.h>
#include <sys/ddi_impldefs.h>
+
+
+
#include "gfx_private.h"
#define MYNAME "gfxp_vgatext"
@@ -151,6 +154,12 @@ static struct fbgattr vgatext_attr = {
#define GFXP_FLAG_CONSOLE 0x00000001
#define GFXP_IS_CONSOLE(softc) ((softc)->flags & GFXP_FLAG_CONSOLE)
+/*
+ * Global name used to write the softc pointer in, for the
+ * data wrapper vgatext_return_pointers()
+ */
+
+
int gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd,
gfxp_vgatext_softc_ptr_t ptr);
static int vgatext_devinit(struct vgatext_softc *, struct vis_devinit *data);
@@ -168,9 +177,16 @@ static void vgatext_polled_cursor(struct vis_polledio_arg *,
struct vis_conscursor *);
static void vgatext_init(struct vgatext_softc *);
static void vgatext_set_text(struct vgatext_softc *);
+
+static void vgatext_save_text(struct vgatext_softc *softc);
+static void vgatext_restore_textmode(struct vgatext_softc *softc);
+static int vgatext_suspend(struct vgatext_softc *softc);
+static void vgatext_resume(struct vgatext_softc *softc);
+
#if defined(USE_BORDERS)
static void vgatext_init_graphics(struct vgatext_softc *);
#endif
+
static int vgatext_kdsetmode(struct vgatext_softc *softc, int mode);
static void vgatext_setfont(struct vgatext_softc *softc);
static void vgatext_get_cursor(struct vgatext_softc *softc,
@@ -220,7 +236,8 @@ gfxp_check_for_console(dev_info_t *devi, struct vgatext_softc *softc,
if (pci_config_setup(devi, &pci_conf) != DDI_SUCCESS) {
cmn_err(CE_WARN,
- MYNAME ": can't get PCI conf handle");
+ MYNAME
+ ": can't get PCI conf handle");
return;
}
@@ -294,25 +311,30 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
int pci_pcie_bus = 0;
+
switch (cmd) {
case DDI_ATTACH:
- break;
+ break;
case DDI_RESUME:
- return (DDI_SUCCESS);
+ vgatext_resume(softc);
+ return (DDI_SUCCESS);
+
default:
- return (DDI_FAILURE);
+ return (DDI_FAILURE);
}
/* DDI_ATTACH */
+ softc->devi = devi; /* Copy and init DEVI */
+
softc->polledio.arg = (struct vis_polledio_arg *)softc;
softc->polledio.display = vgatext_polled_display;
softc->polledio.copy = vgatext_polled_copy;
softc->polledio.cursor = vgatext_polled_cursor;
error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi),
- DDI_PROP_DONTPASS, "device_type", &parent_type);
+ DDI_PROP_DONTPASS, "device_type", &parent_type);
if (error != DDI_SUCCESS) {
cmn_err(CE_WARN, MYNAME ": can't determine parent type.");
goto fail;
@@ -321,46 +343,50 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
/* Not enable AGP and DRM by default */
if (STREQ(parent_type, "isa") || STREQ(parent_type, "eisa")) {
reg_rnumber = vgatext_get_isa_reg_index(devi, 1, VGA_REG_ADDR,
- &reg_offset);
+ &reg_offset);
if (reg_rnumber < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for registers");
+ MYNAME
+ ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
softc->fb_regno = vgatext_get_isa_reg_index(devi, 0,
- VGA_MEM_ADDR, &mem_offset);
+ VGA_MEM_ADDR, &mem_offset);
if (softc->fb_regno < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for memory");
+ MYNAME
+ ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
} else if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) {
pci_pcie_bus = 1;
reg_rnumber = vgatext_get_pci_reg_index(devi,
- PCI_REG_ADDR_M|PCI_REG_REL_M,
- PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
- &reg_offset);
+ PCI_REG_ADDR_M|PCI_REG_REL_M,
+ PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
+ &reg_offset);
if (reg_rnumber < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for registers");
+ MYNAME
+ ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
softc->fb_regno = vgatext_get_pci_reg_index(devi,
- PCI_REG_ADDR_M|PCI_REG_REL_M,
- PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
- &mem_offset);
+ PCI_REG_ADDR_M|PCI_REG_REL_M,
+ PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
+ &mem_offset);
if (softc->fb_regno < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for memory");
+ MYNAME
+ ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
} else {
cmn_err(CE_WARN, MYNAME ": unknown parent type \"%s\".",
- parent_type);
+ parent_type);
error = DDI_FAILURE;
goto fail;
}
@@ -368,8 +394,8 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
parent_type = NULL;
error = ddi_regs_map_setup(devi, reg_rnumber,
- (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE,
- &dev_attr, &softc->regs.handle);
+ (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE,
+ &dev_attr, &softc->regs.handle);
if (error != DDI_SUCCESS)
goto fail;
softc->regs.mapped = B_TRUE;
@@ -377,9 +403,9 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
softc->fb_size = VGA_MEM_SIZE;
error = ddi_regs_map_setup(devi, softc->fb_regno,
- (caddr_t *)&softc->fb.addr,
- mem_offset, softc->fb_size,
- &dev_attr, &softc->fb.handle);
+ (caddr_t *)&softc->fb.addr,
+ mem_offset, softc->fb_size,
+ &dev_attr, &softc->fb.handle);
if (error != DDI_SUCCESS)
goto fail;
softc->fb.mapped = B_TRUE;
@@ -397,7 +423,7 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
goto fail;
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
- DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
+ DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
if (strcmp(cons, "graphics") == 0) {
happyface_boot = 1;
vgatext_silent = 1;
@@ -429,7 +455,15 @@ gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd,
{
struct vgatext_softc *softc = (struct vgatext_softc *)ptr;
+
+
+
+
switch (cmd) {
+
+ case DDI_SUSPEND:
+ return (vgatext_suspend(softc));
+ /* break; */
case DDI_DETACH:
if (softc->fb.mapped)
ddi_regs_map_free(&softc->fb.handle);
@@ -439,7 +473,7 @@ gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd,
default:
cmn_err(CE_WARN, "gfxp_vgatext_detach: unknown cmd 0x%x\n",
- cmd);
+ cmd);
return (DDI_FAILURE);
}
}
@@ -478,7 +512,7 @@ gfxp_vgatext_ioctl(
{
struct vgatext_softc *softc = (struct vgatext_softc *)ptr;
static char kernel_only[] =
- "gfxp_vgatext_ioctl: %s is a kernel only ioctl";
+ "gfxp_vgatext_ioctl: %s is a kernel only ioctl";
int err;
int kd_mode;
@@ -494,58 +528,59 @@ gfxp_vgatext_ioctl(
case VIS_DEVINIT:
- if (!(mode & FKIOCTL)) {
- cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
- return (ENXIO);
- }
+ if (!(mode & FKIOCTL)) {
+ cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
+ return (ENXIO);
+ }
- err = vgatext_devinit(softc, (struct vis_devinit *)data);
- if (err != 0) {
- cmn_err(CE_WARN,
- "gfxp_vgatext_ioctl: could not initialize console");
- return (err);
- }
- break;
+ err = vgatext_devinit(softc, (struct vis_devinit *)data);
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "gfxp_vgatext_ioctl: could not"
+ " initialize console");
+ return (err);
+ }
+ break;
case VIS_CONSCOPY: /* move */
{
- struct vis_conscopy pma;
+ struct vis_conscopy pma;
- if (ddi_copyin((void *)data, &pma,
- sizeof (struct vis_conscopy), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &pma,
+ sizeof (struct vis_conscopy), mode))
+ return (EFAULT);
- vgatext_cons_copy(softc, &pma);
- break;
+ vgatext_cons_copy(softc, &pma);
+ break;
}
case VIS_CONSDISPLAY: /* display */
{
- struct vis_consdisplay display_request;
+ struct vis_consdisplay display_request;
- if (ddi_copyin((void *)data, &display_request,
- sizeof (display_request), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &display_request,
+ sizeof (display_request), mode))
+ return (EFAULT);
- vgatext_cons_display(softc, &display_request);
- break;
+ vgatext_cons_display(softc, &display_request);
+ break;
}
case VIS_CONSCURSOR:
{
- struct vis_conscursor cursor_request;
+ struct vis_conscursor cursor_request;
- if (ddi_copyin((void *)data, &cursor_request,
- sizeof (cursor_request), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &cursor_request,
+ sizeof (cursor_request), mode))
+ return (EFAULT);
- vgatext_cons_cursor(softc, &cursor_request);
+ vgatext_cons_cursor(softc, &cursor_request);
- if (cursor_request.action == VIS_GET_CURSOR &&
- ddi_copyout(&cursor_request, (void *)data,
- sizeof (cursor_request), mode))
- return (EFAULT);
- break;
+ if (cursor_request.action == VIS_GET_CURSOR &&
+ ddi_copyout(&cursor_request, (void *)data,
+ sizeof (cursor_request), mode))
+ return (EFAULT);
+ break;
}
case VIS_GETCMAP:
@@ -576,6 +611,92 @@ gfxp_vgatext_ioctl(
return (0);
}
+/*
+ * vgatext_save_text
+ * vgatext_restore_textmode
+ * vgatext_suspend
+ * vgatext_resume
+ *
+ * Routines to save and restore contents of the VGA text area
+ * Mostly, this is to support Suspend/Resume operation for graphics
+ * device drivers. Here in the VGAtext common code, we simply squirrel
+ * away the contents of the hardware's text area during Suspend and then
+ * put it back during Resume
+ */
+static void
+vgatext_save_text(struct vgatext_softc *softc)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof (softc->shadow); i++)
+ softc->shadow[i] = softc->text_base[i];
+}
+
+static void
+vgatext_restore_textmode(struct vgatext_softc *softc)
+{
+ unsigned i;
+
+ vgatext_init(softc);
+ for (i = 0; i < sizeof (softc->shadow); i++) {
+ softc->text_base[i] = softc->shadow[i];
+ }
+ if (softc->cursor.visible) {
+ vgatext_set_cursor(softc,
+ softc->cursor.row, softc->cursor.col);
+ }
+ vgatext_restore_colormap(softc);
+}
+
+static int
+vgatext_suspend(struct vgatext_softc *softc)
+{
+ switch (softc->mode) {
+ case KD_TEXT:
+ vgatext_save_text(softc);
+ break;
+
+ case KD_GRAPHICS:
+ break;
+
+ default:
+ cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_suspend.");
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+}
+
+static void
+vgatext_resume(struct vgatext_softc *softc)
+{
+
+ switch (softc->mode) {
+ case KD_TEXT:
+ vgatext_restore_textmode(softc);
+ break;
+
+ case KD_GRAPHICS:
+
+ /*
+ * Upon RESUME, the graphics device will always actually
+ * be in TEXT mode even though the Xorg server did not
+ * make that mode change itself (the suspend code did).
+ * We want first, therefore, to restore textmode
+ * operation fully, and then the Xorg server will
+ * do the rest to restore the device to its
+ * (hi resolution) graphics mode
+ */
+ vgatext_restore_textmode(softc);
+#if defined(USE_BORDERS)
+ vgatext_init_graphics(softc);
+#endif
+ break;
+ default:
+ cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_resume.");
+ break;
+ }
+}
+
static int
vgatext_kdsetmode(struct vgatext_softc *softc, int mode)
{
@@ -593,21 +714,23 @@ vgatext_kdsetmode(struct vgatext_softc *softc, int mode)
softc->current_base = softc->text_base;
if (softc->cursor.visible) {
vgatext_set_cursor(softc,
- softc->cursor.row, softc->cursor.col);
+ softc->cursor.row, softc->cursor.col);
}
vgatext_restore_colormap(softc);
break;
case KD_GRAPHICS:
+
+
if (vgatext_silent == 1) {
extern void progressbar_stop(void);
vgatext_silent = 0;
progressbar_stop();
}
- for (i = 0; i < sizeof (softc->shadow); i++) {
- softc->shadow[i] = softc->text_base[i];
- }
+ vgatext_save_text(softc);
+
+
softc->current_base = softc->shadow;
#if defined(USE_BORDERS)
vgatext_init_graphics(softc);
@@ -637,7 +760,7 @@ gfxp_vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
}
if (!(off >= VGA_MMAP_FB_BASE &&
- off < VGA_MMAP_FB_BASE + softc->fb_size)) {
+ off < VGA_MMAP_FB_BASE + softc->fb_size)) {
cmn_err(CE_WARN, "vgatext: Can't map offset 0x%llx", off);
return (-1);
}
@@ -647,9 +770,9 @@ gfxp_vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
else
length = len;
- if ((err = devmap_devmem_setup(dhp, softc->devi, NULL, softc->fb_regno,
- off - VGA_MMAP_FB_BASE,
- length, PROT_ALL, 0, &dev_attr)) < 0) {
+ if ((err = devmap_devmem_setup(dhp, softc->devi,
+ NULL, softc->fb_regno, off - VGA_MMAP_FB_BASE,
+ length, PROT_ALL, 0, &dev_attr)) < 0) {
return (err);
}
@@ -709,10 +832,10 @@ vgatext_cons_display(struct vgatext_softc *softc, struct vis_consdisplay *da)
* system startup graphics.
*/
attr = (solaris_color_to_pc_color[da->bg_color & 0xf] << 4)
- | solaris_color_to_pc_color[da->fg_color & 0xf];
+ | solaris_color_to_pc_color[da->fg_color & 0xf];
string = da->data;
addr = (struct cgatext *)softc->current_base
- + (da->row * TEXT_COLS + da->col);
+ + (da->row * TEXT_COLS + da->col);
for (i = 0; i < da->width; i++) {
addr->ch = string[i];
addr->attr = attr;
@@ -915,20 +1038,20 @@ vgatext_set_text(struct vgatext_softc *softc)
/* set sequencer registers */
vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN,
- (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) &
- ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
+ (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) &
+ ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
for (i = 1; i < NUM_SEQ_REG; i++) {
vga_set_seq(&softc->regs, i, VGA_SEQ_TEXT[i]);
}
vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN,
- (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) |
- VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
- VGA_SEQ_RST_SYN_NO_SYNC_RESET));
+ (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) |
+ VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
+ VGA_SEQ_RST_SYN_NO_SYNC_RESET));
/* set crt controller registers */
vga_set_crtc(&softc->regs, VGA_CRTC_VRE,
- (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) &
- ~VGA_CRTC_VRE_LOCK));
+ (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) &
+ ~VGA_CRTC_VRE_LOCK));
for (i = 0; i < NUM_CRTC_REG; i++) {
vga_set_crtc(&softc->regs, i, VGA_CRTC_TEXT[i]);
}
@@ -946,8 +1069,8 @@ vgatext_set_text(struct vgatext_softc *softc)
/* set palette */
for (i = 0; i < VGA_TEXT_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i, VGA_TEXT_PALETTES[i][0] << 2,
- VGA_TEXT_PALETTES[i][1] << 2,
- VGA_TEXT_PALETTES[i][2] << 2);
+ VGA_TEXT_PALETTES[i][1] << 2,
+ VGA_TEXT_PALETTES[i][2] << 2);
}
for (i = VGA_TEXT_CMAP_ENTRIES; i < VGA8_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i, 0, 0, 0);
@@ -970,10 +1093,10 @@ vgatext_init(struct vgatext_softc *softc)
vga_set_atr(&softc->regs, VGA_ATR_MODE, atr_mode);
#if defined(USE_BORDERS)
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE));
+ vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE));
#else
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BLACK));
+ vga_get_atr(&softc->regs, VGA_BLACK));
#endif
vgatext_setfont(softc); /* need selectable font? */
}
@@ -983,7 +1106,7 @@ static void
vgatext_init_graphics(struct vgatext_softc *softc)
{
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BLACK));
+ vga_get_atr(&softc->regs, VGA_BLACK));
}
#endif
@@ -1044,9 +1167,9 @@ vgatext_save_colormap(struct vgatext_softc *softc)
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
vga_get_cmap(&softc->regs, i,
- &softc->colormap[i].red,
- &softc->colormap[i].green,
- &softc->colormap[i].blue);
+ &softc->colormap[i].red,
+ &softc->colormap[i].green,
+ &softc->colormap[i].blue);
}
}
@@ -1060,9 +1183,9 @@ vgatext_restore_colormap(struct vgatext_softc *softc)
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i,
- softc->colormap[i].red,
- softc->colormap[i].green,
- softc->colormap[i].blue);
+ softc->colormap[i].red,
+ softc->colormap[i].green,
+ softc->colormap[i].blue);
}
}
@@ -1097,7 +1220,7 @@ vgatext_get_pci_reg_index(
pci_regspec_t *reg;
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
return (-1);
}
@@ -1156,7 +1279,7 @@ vgatext_get_isa_reg_index(
struct regspec *reg;
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
return (-1);
}
@@ -1176,3 +1299,22 @@ vgatext_get_isa_reg_index(
return (-1);
}
+
+/*
+ * This vgatext function is used to return the fb, and reg pointers
+ * and handles for peer graphics drivers.
+ */
+
+void
+vgatext_return_pointers(struct vgatext_softc *softc,
+ struct vgaregmap *fbs,
+ struct vgaregmap *regss)
+{
+
+ fbs->addr = softc->fb.addr;
+ fbs->handle = softc->fb.handle;
+ fbs->mapped = softc->fb.mapped;
+ regss->addr = softc->regs.addr;
+ regss->handle = softc->regs.handle;
+ regss->mapped = softc->regs.mapped;
+}
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_drv.c b/usr/src/uts/i86pc/io/mc/mcamd_drv.c
index d7c9371795..284de242a9 100644
--- a/usr/src/uts/i86pc/io/mc/mcamd_drv.c
+++ b/usr/src/uts/i86pc/io/mc/mcamd_drv.c
@@ -1491,6 +1491,17 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
int chipid, rc;
mc_t *mc;
+ /*
+ * This driver has no hardware state, but does
+ * claim to have a reg property, so it will be
+ * called on suspend. It is probably better to
+ * make sure it doesn't get called on suspend,
+ * but it is just as easy to make sure we just
+ * return DDI_SUCCESS if called.
+ */
+ if (cmd == DDI_RESUME)
+ return (DDI_SUCCESS);
+
if (cmd != DDI_ATTACH || mc_no_attach != 0)
return (DDI_FAILURE);
@@ -1672,7 +1683,14 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
static int
mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
- return (DDI_FAILURE);
+ /*
+ * See the comment about suspend in
+ * mc_attach().
+ */
+ if (cmd == DDI_SUSPEND)
+ return (DDI_SUCCESS);
+ else
+ return (DDI_FAILURE);
}
static struct dev_ops mc_ops = {
diff --git a/usr/src/uts/i86pc/io/mp_platform_common.c b/usr/src/uts/i86pc/io/mp_platform_common.c
index 7acbca94a8..563b6b7cdd 100644
--- a/usr/src/uts/i86pc/io/mp_platform_common.c
+++ b/usr/src/uts/i86pc/io/mp_platform_common.c
@@ -30,8 +30,9 @@
* PSMI 1.2 extensions are supported only in 2.7 and later versions.
* PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
* PSMI 1.5 extensions are supported in Solaris Nevada.
+ * PSMI 1.6 extensions are supported in Solaris Nevada.
*/
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/processor.h>
#include <sys/time.h>
@@ -3989,3 +3990,182 @@ apic_is_ioapic_AMD_813x(uint32_t physaddr)
ndi_rele_devi(ioapicsnode);
return (rv);
}
+
+struct apic_state {
+ int32_t as_task_reg;
+ int32_t as_dest_reg;
+ int32_t as_format_reg;
+ int32_t as_local_timer;
+ int32_t as_pcint_vect;
+ int32_t as_int_vect0;
+ int32_t as_int_vect1;
+ int32_t as_err_vect;
+ int32_t as_init_count;
+ int32_t as_divide_reg;
+ int32_t as_spur_int_reg;
+ int32_t as_ioapic[6][24]; /* spec says 23 */
+};
+
+
+static void
+apic_save_state(struct apic_state *sp)
+{
+ int i;
+
+ PMD(PMD_SX, ("apic_save_state %p\n", (void *)sp))
+ /*
+ * First the local APIC.
+ */
+ sp->as_task_reg = apicadr[APIC_TASK_REG];
+ sp->as_dest_reg = apicadr[APIC_DEST_REG];
+ sp->as_format_reg = apicadr[APIC_FORMAT_REG];
+ sp->as_local_timer = apicadr[APIC_LOCAL_TIMER];
+ sp->as_pcint_vect = apicadr[APIC_PCINT_VECT];
+ sp->as_int_vect0 = apicadr[APIC_INT_VECT0];
+ sp->as_int_vect1 = apicadr[APIC_INT_VECT1];
+ sp->as_err_vect = apicadr[APIC_ERR_VECT];
+ sp->as_init_count = apicadr[APIC_INIT_COUNT];
+ sp->as_divide_reg = apicadr[APIC_DIVIDE_REG];
+ sp->as_spur_int_reg = apicadr[APIC_SPUR_INT_REG];
+
+ /*
+ * if on the boot processor then save the IO APICs.
+ */
+ if (psm_get_cpu_id() == 0) {
+ for (i = 0; i < apic_io_max; i++) {
+ volatile uint32_t *ioapic = apicioadr[i];
+ int intin_max, j;
+
+ /* Bits 23-16 define the maximum redirection entries */
+ ioapic[APIC_IO_REG] = APIC_VERS_CMD;
+ intin_max = (ioapic[APIC_IO_DATA] >> 16) & 0xff;
+#if 0 /* debug */
+ prom_printf("\nIOAPIC %d (%d redirs):\n",
+ i, intin_max+1);
+#endif /* debug */
+ for (j = 0; j <= intin_max; j++) {
+ ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2*j;
+ sp->as_ioapic[i][j] = ioapic[APIC_IO_DATA];
+#if 0 /* debug */
+ prom_printf("\t%d: %x\n", j, as_ioapic[i][j]);
+#endif /* debug */
+ }
+ }
+ }
+}
+
+static void
+apic_restore_state(struct apic_state *sp)
+{
+ int i;
+ int iflag;
+ apic_irq_t *irqp;
+ int rv;
+ int retval = 0;
+
+ /*
+ * First the local APIC.
+ */
+ apicadr[APIC_TASK_REG] = sp->as_task_reg;
+ apicadr[APIC_DEST_REG] = sp->as_dest_reg;
+ apicadr[APIC_FORMAT_REG] = sp->as_format_reg;
+ apicadr[APIC_LOCAL_TIMER] = sp->as_local_timer;
+ apicadr[APIC_PCINT_VECT] = sp->as_pcint_vect;
+ apicadr[APIC_INT_VECT0] = sp->as_int_vect0;
+ apicadr[APIC_INT_VECT1] = sp->as_int_vect1;
+ apicadr[APIC_ERR_VECT] = sp->as_err_vect;
+ apicadr[APIC_INIT_COUNT] = sp->as_init_count;
+ apicadr[APIC_DIVIDE_REG] = sp->as_divide_reg;
+ apicadr[APIC_SPUR_INT_REG] = sp->as_spur_int_reg;
+
+ /*
+ * the following only needs to be done once, so we do it on the
+ * boot processor, since we know that we only have one of those
+ */
+ if (psm_get_cpu_id() == 0) {
+ /*
+ * regenerate the IO APICs.
+ */
+
+ iflag = intr_clear();
+ lock_set(&apic_ioapic_lock);
+
+ for (i = apic_min_device_irq; i < apic_max_device_irq; i++) {
+ if ((irqp = apic_irq_table[i]) == NULL)
+ continue;
+ for (; irqp; irqp = irqp->airq_next) {
+ if (irqp->airq_mps_intr_index == FREE_INDEX)
+ continue;
+ if (irqp->airq_temp_cpu != IRQ_UNINIT) {
+ rv = apic_setup_io_intr(irqp, i,
+ B_FALSE);
+ if (rv) {
+ PMD(PMD_SX,
+ ("apic_setup_io_intr(%p, "
+ "%d) %d\n", (void *)irqp,
+ i, rv));
+ }
+ retval |= rv;
+ }
+ }
+ }
+
+ PMD(PMD_SX, ("apic_restore_state retval %x\n", retval))
+
+ lock_clear(&apic_ioapic_lock);
+ intr_restore(iflag);
+
+
+ /*
+ * restore acpi link device mappings
+ */
+ acpi_restore_link_devices();
+ }
+}
+
+/*
+ * Returns 0 on success
+ */
+int
+apic_state(psm_state_request_t *rp)
+{
+ PMD(PMD_SX, ("apic_state "))
+ switch (rp->psr_cmd) {
+ case PSM_STATE_ALLOC:
+ rp->req.psm_state_req.psr_state =
+ kmem_zalloc(sizeof (struct apic_state), KM_NOSLEEP);
+ if (rp->req.psm_state_req.psr_state == NULL)
+ return (ENOMEM);
+ rp->req.psm_state_req.psr_state_size =
+ sizeof (struct apic_state);
+ PMD(PMD_SX, (":STATE_ALLOC: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ return (0);
+
+ case PSM_STATE_FREE:
+ kmem_free(rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size);
+ PMD(PMD_SX, (" STATE_FREE: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ return (0);
+
+ case PSM_STATE_SAVE:
+ PMD(PMD_SX, (" STATE_SAVE: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ apic_save_state(rp->req.psm_state_req.psr_state);
+ return (0);
+
+ case PSM_STATE_RESTORE:
+ apic_restore_state(rp->req.psm_state_req.psr_state);
+ PMD(PMD_SX, (" STATE_RESTORE: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ return (0);
+
+ default:
+ return (EINVAL);
+ }
+}
diff --git a/usr/src/uts/i86pc/io/pci/pci.c b/usr/src/uts/i86pc/io/pci/pci.c
index 1af5ca17fb..7cd0b38d4e 100644
--- a/usr/src/uts/i86pc/io/pci/pci.c
+++ b/usr/src/uts/i86pc/io/pci/pci.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -221,6 +221,16 @@ pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
*/
int instance = ddi_get_instance(devi);
pci_state_t *pcip = NULL;
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type", "pci")
!= DDI_PROP_SUCCESS) {
@@ -285,23 +295,30 @@ pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
pci_state_t *pcip;
pcip = ddi_get_soft_state(pci_statep, ddi_get_instance(devi));
- if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) {
- ddi_fm_handler_unregister(devi);
- pci_ereport_teardown(devi);
- }
- mutex_destroy(&pcip->pci_peek_poke_mutex);
- mutex_destroy(&pcip->pci_err_mutex);
- ddi_fm_fini(devi);
- /* Uninitialize pcitool support. */
- pcitool_uninit(devi);
- /* Uninitialize hotplug support on this bus. */
- (void) pcihp_uninit(devi);
+ switch (cmd) {
+ case DDI_DETACH:
+ if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) {
+ ddi_fm_handler_unregister(devi);
+ pci_ereport_teardown(devi);
+ }
+ mutex_destroy(&pcip->pci_peek_poke_mutex);
+ mutex_destroy(&pcip->pci_err_mutex);
+ ddi_fm_fini(devi); /* Uninitialize pcitool support. */
+ pcitool_uninit(devi);
- ddi_soft_state_free(pci_statep, instance);
+ /* Uninitialize hotplug support on this bus. */
+ (void) pcihp_uninit(devi);
- return (DDI_SUCCESS);
+ ddi_soft_state_free(pci_statep, instance);
+
+ return (DDI_SUCCESS);
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
}
static int
@@ -510,6 +527,7 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
int rn;
int totreg;
pci_state_t *pcip;
+ struct attachspec *asp;
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
@@ -537,8 +555,8 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
*(int *)result = 0;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
- DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
- &reglen) != DDI_PROP_SUCCESS) {
+ DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
+ &reglen) != DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
@@ -578,6 +596,21 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
pci_common_peekpoke, &pcip->pci_err_mutex,
&pcip->pci_peek_poke_mutex));
+ /* for now only X86 systems support PME wakeup from suspended state */
+ case DDI_CTLOPS_ATTACH:
+ asp = (struct attachspec *)arg;
+ if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
+ if (pci_pre_resume(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+ case DDI_CTLOPS_DETACH:
+ asp = (struct attachspec *)arg;
+ if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST)
+ if (pci_post_suspend(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
default:
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
}
@@ -679,8 +712,7 @@ pci_initchild(dev_info_t *child)
* Support for the "command-preserve" property.
*/
command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
- DDI_PROP_DONTPASS,
- "command-preserve", 0);
+ DDI_PROP_DONTPASS, "command-preserve", 0);
command = pci_config_get16(config_handle, PCI_CONF_COMM);
command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
command |= (pci_command_default & ~command_preserve);
diff --git a/usr/src/uts/i86pc/io/pciex/npe.c b/usr/src/uts/i86pc/io/pciex/npe.c
index 80ff196f44..fde4276cc7 100644
--- a/usr/src/uts/i86pc/io/pciex/npe.c
+++ b/usr/src/uts/i86pc/io/pciex/npe.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -228,6 +228,9 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
int instance = ddi_get_instance(devi);
pci_state_t *pcip = NULL;
+ if (cmd == DDI_RESUME)
+ return (DDI_SUCCESS);
+
if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type",
"pciex") != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "npe: 'device_type' prop create failed");
@@ -282,20 +285,29 @@ npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
pcip = ddi_get_soft_state(npe_statep, ddi_get_instance(devi));
- /* Uninitialize pcitool support. */
- pcitool_uninit(devi);
+ switch (cmd) {
+ case DDI_DETACH:
- /*
- * Uninitialize hotplug support on this bus.
- */
- (void) pcihp_uninit(devi);
+ /* Uninitialize pcitool support. */
+ pcitool_uninit(devi);
- if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
- ddi_fm_handler_unregister(devi);
+ /*
+ * Uninitialize hotplug support on this bus.
+ */
+ (void) pcihp_uninit(devi);
- ddi_fm_fini(devi);
- ddi_soft_state_free(npe_statep, instance);
- return (DDI_SUCCESS);
+ if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
+ ddi_fm_handler_unregister(devi);
+
+ ddi_fm_fini(devi);
+ ddi_soft_state_free(npe_statep, instance);
+ return (DDI_SUCCESS);
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
}
@@ -598,6 +610,7 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
int totreg;
uint_t reglen;
pci_regspec_t *drv_regp;
+ struct attachspec *asp;
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
@@ -662,6 +675,30 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
case DDI_CTLOPS_POKE:
return (pci_common_peekpoke(dip, rdip, ctlop, arg, result));
+ /* X86 systems support PME wakeup from suspended state */
+ case DDI_CTLOPS_ATTACH:
+ asp = (struct attachspec *)arg;
+ /* only do this for immediate children */
+ if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE &&
+ ddi_get_parent(rdip) == dip)
+ if (pci_pre_resume(rdip) != DDI_SUCCESS) {
+ /* Not good, better stop now. */
+ cmn_err(CE_PANIC,
+ "Couldn't pre-resume device %p",
+ (void *) dip);
+ /* NOTREACHED */
+ }
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+ case DDI_CTLOPS_DETACH:
+ asp = (struct attachspec *)arg;
+ /* only do this for immediate children */
+ if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST &&
+ ddi_get_parent(rdip) == dip)
+ if (pci_post_suspend(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
default:
break;
}
diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic.c b/usr/src/uts/i86pc/io/pcplusmp/apic.c
index f31aa2dbcc..e63f2d596a 100644
--- a/usr/src/uts/i86pc/io/pcplusmp/apic.c
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c
@@ -31,8 +31,9 @@
* PSMI 1.2 extensions are supported only in 2.7 and later versions.
* PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
* PSMI 1.5 extensions are supported in Solaris Nevada.
+ * PSMI 1.6 extensions are supported in Solaris Nevada.
*/
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/processor.h>
#include <sys/time.h>
@@ -238,12 +239,13 @@ static struct psm_ops apic_ops = {
apic_timer_disable,
apic_post_cyclic_setup,
apic_preshutdown,
- apic_intr_ops /* Advanced DDI Interrupt framework */
+ apic_intr_ops, /* Advanced DDI Interrupt framework */
+ apic_state, /* save, restore apic state for S3 */
};
static struct psm_info apic_psm_info = {
- PSM_INFO_VER01_5, /* version */
+ PSM_INFO_VER01_6, /* version */
PSM_OWN_EXCLUSIVE, /* ownership */
(struct psm_ops *)&apic_ops, /* operation */
APIC_PCPLUSMP_NAME, /* machine name */
@@ -1371,6 +1373,9 @@ apic_preshutdown(int cmd, int fcn)
APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n",
cmd, fcn, apic_poweroff_method, apic_enable_acpi));
+ if ((cmd != A_SHUTDOWN) || (fcn != AD_POWEROFF)) {
+ return;
+ }
}
static void
diff --git a/usr/src/uts/i86pc/io/ppm.conf b/usr/src/uts/i86pc/io/ppm.conf
index 830e94a28a..ea1f555526 100644
--- a/usr/src/uts/i86pc/io/ppm.conf
+++ b/usr/src/uts/i86pc/io/ppm.conf
@@ -34,6 +34,7 @@ name="ppm" parent="pseudo" instance=0;
# the nature of the domain;
#
# "domain_xxx-model" - PM model: CPU
+# "domain_xxx-model" - PM model: SX
#
# "domain_xxx-propname" - a property name that is exported by device in
# a domain. Currently, it is used by PCI_PROP model to identify devices
@@ -58,7 +59,7 @@ name="ppm" parent="pseudo" instance=0;
#
# which keywords apply depends on cmd. There are two sets as shown below.
# Here is the first:
-# cmd=[PCI_ON | PCI_OFF]
+# cmd=[ENTER_S3 | ENTER_S4]
# path=<prompath> - control device's prom pathname (includes minor)
# method=[KIO|I2CKIO] This selects a method which may be
# an ioctl that sets a single value or an i2c ioctl that
@@ -69,7 +70,48 @@ name="ppm" parent="pseudo" instance=0;
# the relevant bits of a register will be set
# mask=<integer> - which bits of val are relevant (if method is I2CKIO)
#
+ppm-domains="domain_cpu" , "domain_estar";
-ppm-domains="domain_cpu";
+#
+# CPU domain
+#
domain_cpu-devices="/cpus/cpu@*";
domain_cpu-model="CPU";
+
+#
+# Estar domain
+# 0x4101 is APPMIOC_ENTER_S3 (('A' << 8) | 1)
+# 0x4102 is APPMIOC_EXIT_S3 (('A' << 8) | 2)
+#
+domain_estar-devices="/";
+domain_estar-model="SX";
+domain_estar-control=
+ "cmd=ENTER_S3 path=/pseudo/acpippm@0:acpi-ppm method=KIO iowr=0x4101",
+ "cmd=EXIT_S3 path=/pseudo/acpippm@0:acpi-ppm method=KIO iowr=0x4102";
+
+#
+# S3-enable whitelist
+#
+S3-support-enable =
+ "Sun Microsystems", "Sun Ultra 40 Workstation",
+ "Sun Microsystems", "Sun Ultra 20 Workstation";
+
+S3-support-disable =
+ "Sun Microsystems", "Sun Blade x8400 Server Module",
+ "Sun Microsystems", "Sun Fire*";
+
+S3-autoenable =
+ "Sun Microsystems", "Sun Ultra 40 Workstation",
+ "Sun Microsystems", "Sun Ultra 20 Workstation";
+
+S3-autodisable =
+ "Sun Microsystems", "Sun Blade x8400 Server Module",
+ "Sun Microsystems", "Sun Fire*";
+
+autopm-enable =
+ "Sun Microsystems", "Sun Ultra 40 Workstation",
+ "Sun Microsystems", "Sun Ultra 20 Workstation";
+
+autopm-disable =
+ "Sun Microsystems", "Sun Blade x8400 Server Module",
+ "Sun Microsystems", "Sun Fire*";
diff --git a/usr/src/uts/i86pc/io/ppm/acpippm.c b/usr/src/uts/i86pc/io/ppm/acpippm.c
new file mode 100644
index 0000000000..a8e5019e50
--- /dev/null
+++ b/usr/src/uts/i86pc/io/ppm/acpippm.c
@@ -0,0 +1,443 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/open.h>
+#include <sys/modctl.h>
+#include <sys/promif.h>
+#include <sys/stat.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/epm.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+#include <sys/psm_types.h>
+
+/*
+ * ACPI Power Management Driver
+ *
+ * acpippm deals with those bits of ppm functionality that
+ * must be mediated by ACPI
+ *
+ * The routines in this driver is referenced by Platform
+ * Power Management driver of X86 workstation systems.
+ * acpippm driver is loaded because it is listed as a platform driver
+ * It is initially configured as a pseudo driver.
+ */
+
+/*
+ * Configuration Function prototypes and data structures
+ */
+static int appm_attach(dev_info_t *, ddi_attach_cmd_t);
+static int appm_detach(dev_info_t *, ddi_detach_cmd_t);
+static int appm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
+static int appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
+static int appm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+/*
+ * Configuration data structures
+ */
+static struct cb_ops appm_cbops = {
+ appm_open, /* open */
+ appm_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ appm_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* chpoll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* stream */
+ D_MP | D_NEW, /* flag */
+ CB_REV, /* rev */
+ nodev, /* aread */
+ nodev, /* awrite */
+};
+
+static struct dev_ops appm_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ appm_getinfo, /* getinfo */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ appm_attach, /* attach */
+ appm_detach, /* detach */
+ nodev, /* reset */
+ &appm_cbops, /* cb_ops */
+ NULL, /* bus_ops */
+ NULL /* power */
+};
+
+extern struct mod_ops mod_driverops;
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "ACPI ppm driver v1.8",
+ &appm_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+/*
+ * Driver state structure
+ */
+typedef struct {
+ dev_info_t *dip;
+ ddi_acc_handle_t devid_hndl;
+ ddi_acc_handle_t estar_hndl;
+ int lyropen; /* ref count */
+} appm_unit;
+
+/*
+ * Driver global variables
+ *
+ * appm_lock synchronize the access of lyr handle to each appm
+ * minor device, therefore write to tomatillo device is
+ * sequentialized. Lyr protocol requires pairing up lyr open
+ * and close, so only a single reference is allowed per minor node.
+ */
+static void *appm_statep;
+static kmutex_t appm_lock;
+
+/*
+ * S3 stuff:
+ */
+char _depends_on[] = "misc/acpica";
+
+extern int acpi_enter_sleepstate(s3a_t *);
+extern int acpi_exit_sleepstate(s3a_t *);
+
+
+int
+_init(void)
+{
+ int error;
+
+ if ((error = ddi_soft_state_init(&appm_statep,
+ sizeof (appm_unit), 0)) != DDI_SUCCESS) {
+ return (error);
+ }
+
+ mutex_init(&appm_lock, NULL, MUTEX_DRIVER, NULL);
+
+ if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) {
+ mutex_destroy(&appm_lock);
+ ddi_soft_state_fini(&appm_statep);
+ return (error);
+ }
+
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) {
+ mutex_destroy(&appm_lock);
+ ddi_soft_state_fini(&appm_statep);
+ }
+
+ return (error);
+
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+
+/*
+ * Driver attach(9e) entry point
+ */
+static int
+appm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ char *str = "appm_attach";
+ int instance;
+ appm_unit *unitp;
+ int rv = DDI_SUCCESS;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ cmn_err(CE_WARN, "%s: cmd %d unsupported.\n", str, cmd);
+ return (DDI_FAILURE);
+ }
+
+ instance = ddi_get_instance(dip);
+ rv = ddi_soft_state_zalloc(appm_statep, instance);
+ if (rv != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s: failed alloc for dev(%s@%s)",
+ str, ddi_binding_name(dip),
+ ddi_get_name_addr(dip) ? ddi_get_name_addr(dip) : " ");
+ return (rv);
+ }
+
+ if ((unitp = ddi_get_soft_state(appm_statep, instance)) == NULL) {
+ rv = DDI_FAILURE;
+ goto doerrs;
+ }
+
+ /*
+ * Export "ddi-kernel-ioctl" property - prepared to support
+ * kernel ioctls (driver layering).
+ * XXX is this still needed?
+ * XXXX (RSF) Not that I am aware of.
+ */
+ rv = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ DDI_KERNEL_IOCTL, NULL, 0);
+ if (rv != DDI_PROP_SUCCESS)
+ goto doerrs;
+
+ ddi_report_dev(dip);
+ unitp->dip = dip;
+
+ /*
+ * XXX here we would do whatever we need to to determine if the
+ * XXX platform supports ACPI, and fail the attach if not.
+ * XXX If it does, we do whatever setup is needed to get access to
+ * XXX ACPI register space.
+ */
+
+ unitp->lyropen = 0;
+
+ /*
+ * create minor node for kernel_ioctl calls
+ */
+ rv = ddi_create_minor_node(dip, "acpi-ppm", S_IFCHR, instance, 0, 0);
+ if (rv != DDI_SUCCESS)
+ goto doerrs;
+
+ return (rv);
+
+doerrs:
+
+ if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
+ DDI_PROP_NOTPROM, DDI_KERNEL_IOCTL))
+ ddi_prop_remove_all(dip);
+
+ ddi_soft_state_free(appm_statep, instance);
+
+ return (rv);
+}
+
+
+/*
+ * Driver getinfo(9e) entry routine
+ */
+/* ARGSUSED */
+static int
+appm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ appm_unit *unitp;
+ int instance;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ instance = getminor((dev_t)arg);
+ unitp = ddi_get_soft_state(appm_statep, instance);
+ if (unitp == NULL) {
+ return (DDI_FAILURE);
+ }
+ *result = (void *) unitp->dip;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ instance = getminor((dev_t)arg);
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+
+/*
+ * detach(9e)
+ */
+/* ARGSUSED */
+static int
+appm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ char *str = "appm_detach";
+
+ switch (cmd) {
+ case DDI_DETACH:
+ return (DDI_FAILURE);
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ cmn_err(CE_WARN, "%s: cmd %d unsupported", str, cmd);
+ return (DDI_FAILURE);
+ }
+}
+
+
+/* ARGSUSED */
+static int
+appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
+{
+ appm_unit *unitp;
+
+ /* not intended to allow sysadmin level root process to open it */
+ if (drv_priv(cred_p) != DDI_SUCCESS)
+ return (EPERM);
+
+ if ((unitp = ddi_get_soft_state(
+ appm_statep, getminor(*dev_p))) == NULL) {
+ cmn_err(CE_WARN, "appm_open: failed to get soft state!");
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&appm_lock);
+ if (unitp->lyropen != 0) {
+ mutex_exit(&appm_lock);
+ return (EBUSY);
+ }
+ unitp->lyropen++;
+ mutex_exit(&appm_lock);
+
+ return (DDI_SUCCESS);
+}
+
+
+/* ARGSUSED */
+static int
+appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ appm_unit *unitp;
+
+ if ((unitp =
+ ddi_get_soft_state(appm_statep, getminor(dev))) == NULL)
+ return (DDI_FAILURE);
+
+ mutex_enter(&appm_lock);
+ unitp->lyropen = 0;
+ mutex_exit(&appm_lock);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * must match ppm.conf
+ */
+#define APPMIOC ('A' << 8)
+#define APPMIOC_ENTER_S3 (APPMIOC | 1) /* arg *s3a_t */
+#define APPMIOC_EXIT_S3 (APPMIOC | 2) /* arg *s3a_t */
+
+/* ARGSUSED3 */
+static int
+appm_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
+ cred_t *cred_p, int *rval_p)
+{
+ static boolean_t acpi_initted = B_FALSE;
+ char *str = "appm_ioctl";
+ int ret;
+ s3a_t *s3ap = (s3a_t *)arg;
+
+ PMD(PMD_SX, ("%s: called with %x\n", str, cmd))
+
+ if (drv_priv(cred_p) != 0) {
+ PMD(PMD_SX, ("%s: EPERM\n", str))
+ return (EPERM);
+ }
+
+ if (ddi_get_soft_state(appm_statep, getminor(dev)) == NULL) {
+ PMD(PMD_SX, ("%s: no soft state: EIO\n", str))
+ return (EIO);
+ }
+
+ if (!acpi_initted) {
+ PMD(PMD_SX, ("%s: !acpi_initted\n", str))
+ if (acpica_init() == 0) {
+ acpi_initted = B_TRUE;
+ } else {
+ if (rval_p != NULL) {
+ *rval_p = EINVAL;
+ }
+ PMD(PMD_SX, ("%s: EINVAL\n", str))
+ return (EINVAL);
+ }
+ }
+
+ PMD(PMD_SX, ("%s: looking for cmd %x\n", str, cmd))
+ switch (cmd) {
+ case APPMIOC_ENTER_S3:
+ /*
+ * suspend to RAM (ie S3)
+ */
+ PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg))
+ ret = acpi_enter_sleepstate(s3ap);
+ break;
+
+ case APPMIOC_EXIT_S3:
+ /*
+ * return from S3
+ */
+ PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg))
+ ret = acpi_exit_sleepstate(s3ap);
+ break;
+
+ default:
+ PMD(PMD_SX, ("%s: cmd %x unrecognized: ENOTTY\n", str, cmd))
+ return (ENOTTY);
+ }
+
+ /*
+ * upon failure return EINVAL
+ */
+ if (ret != 0) {
+ if (rval_p != NULL) {
+ *rval_p = EINVAL;
+ }
+ return (EINVAL);
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/i86pc/io/ppm/acpippm.h b/usr/src/uts/i86pc/io/ppm/acpippm.h
new file mode 100644
index 0000000000..c60aedfc15
--- /dev/null
+++ b/usr/src/uts/i86pc/io/ppm/acpippm.h
@@ -0,0 +1,41 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_ACPIPPM_H
+#define _SYS_ACPIPPM_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_ACPIPPM_H */
diff --git a/usr/src/uts/i86pc/io/ppm/acpisleep.c b/usr/src/uts/i86pc/io/ppm/acpisleep.c
new file mode 100644
index 0000000000..d1ab6c5c34
--- /dev/null
+++ b/usr/src/uts/i86pc/io/ppm/acpisleep.c
@@ -0,0 +1,214 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/smp_impldefs.h>
+#include <sys/promif.h>
+
+#include <sys/kmem.h>
+#include <sys/archsystm.h>
+#include <sys/cpuvar.h>
+#include <sys/pte.h>
+#include <vm/seg_kmem.h>
+#include <sys/epm.h>
+#include <sys/machsystm.h>
+#include <sys/clock.h>
+
+#include <sys/cpr_wakecode.h>
+#include <sys/acpi/acpi.h>
+
+#ifdef OLDPMCODE
+#include "acpi.h"
+#endif
+
+#include <sys/x86_archext.h>
+#include <sys/reboot.h>
+#include <sys/cpu_module.h>
+#include <sys/kdi.h>
+
+/*
+ * S3 stuff
+ */
+
+int acpi_rtc_wake = 0x0; /* wake in N seconds */
+
+#if 0 /* debug */
+static uint8_t branchbuf[64 * 1024]; /* for the HDT branch trace stuff */
+#endif /* debug */
+
+extern int boothowto;
+
+#define BOOTCPU 0 /* cpu 0 is always the boot cpu */
+
+extern void kernel_wc_code(void);
+extern tod_ops_t *tod_ops;
+extern int flushes_require_xcalls;
+extern int tsc_gethrtime_enable;
+
+extern cpuset_t cpu_ready_set;
+extern void *(*cpu_pause_func)(void *);
+
+/*
+ * This probably belong in apic.c, along with the save/restore stuff.
+ */
+static void
+reinit_picmode(void)
+{
+ ACPI_OBJECT_LIST arglist;
+ ACPI_OBJECT arg;
+ ACPI_STATUS status;
+
+ /* Setup parameter object */
+ arglist.Count = 1;
+ arglist.Pointer = &arg;
+ arg.Type = ACPI_TYPE_INTEGER;
+ arg.Integer.Value = 1;
+
+ status = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ PMD(PMD_SX, ("Method _PIC failed, %d\n", status))
+ }
+}
+
+
+/*
+ * This is what we've all been waiting for!
+ */
+int
+acpi_enter_sleepstate(s3a_t *s3ap)
+{
+ ACPI_PHYSICAL_ADDRESS wakephys = s3ap->s3a_wakephys;
+ caddr_t wakevirt = rm_platter_va;
+ /*LINTED*/
+ wakecode_t *wp = (wakecode_t *)wakevirt;
+ uint_t Sx = s3ap->s3a_state;
+
+ PT(PT_SWV);
+ /* Set waking vector */
+ if (AcpiSetFirmwareWakingVector(wakephys) != AE_OK) {
+ PT(PT_SWV_FAIL);
+ PMD(PMD_SX, ("Can't SetFirmwareWakingVector(%lx)\n",
+ (long)wakephys))
+ goto insomnia;
+ }
+
+ PT(PT_EWE);
+ /* Enable wake events */
+ if (AcpiEnableEvent(ACPI_EVENT_POWER_BUTTON, 0) != AE_OK) {
+ PT(PT_EWE_FAIL);
+ PMD(PMD_SX, ("Can't EnableEvent(POWER_BUTTON)\n"))
+ }
+ if (acpi_rtc_wake > 0) {
+ PT(PT_RTCW);
+ if (AcpiEnableEvent(ACPI_EVENT_RTC, 0) != AE_OK) {
+ PT(PT_RTCW_FAIL);
+ PMD(PMD_SX, ("Can't EnableEvent(RTC)\n"))
+ }
+
+ /*
+ * Set RTC to wake us in a wee while.
+ */
+ mutex_enter(&tod_lock);
+ PT(PT_TOD);
+ TODOP_SETWAKE(tod_ops, acpi_rtc_wake);
+ mutex_exit(&tod_lock);
+ }
+
+ /*
+ * Prepare for sleep ... could've done this earlier?
+ */
+ PT(PT_SXP);
+ PMD(PMD_SX, ("Calling AcpiEnterSleepStatePrep(%d) ...\n", Sx))
+ if (AcpiEnterSleepStatePrep(Sx) != AE_OK) {
+ PMD(PMD_SX, ("... failed\n!"))
+ goto insomnia;
+ }
+
+ switch (s3ap->s3a_test_point) {
+ case DEVICE_SUSPEND_TO_RAM:
+ case LOOP_BACK_PASS:
+ return (0);
+ case LOOP_BACK_FAIL:
+ return (1);
+ default:
+ ASSERT(s3ap->s3a_test_point == LOOP_BACK_NONE ||
+ s3ap->s3a_test_point == FORCE_SUSPEND_TO_RAM);
+ }
+
+ /*
+ * Tell the hardware to sleep.
+ */
+ PT(PT_SXE);
+ PMD(PMD_SX, ("Calling AcpiEnterSleepState(%d) ...\n", Sx))
+ if (AcpiEnterSleepState(Sx) != AE_OK) {
+ PT(PT_SXE_FAIL);
+ PMD(PMD_SX, ("... failed!\n"))
+ }
+
+insomnia:
+ PT(PT_INSOM);
+ /* cleanup is done in the caller */
+ return (1);
+}
+
+int
+acpi_exit_sleepstate(s3a_t *s3ap)
+{
+ int Sx = s3ap->s3a_state;
+
+ PT(PT_WOKE);
+ PMD(PMD_SX, ("!We woke up!\n"))
+
+ PT(PT_LSS);
+ if (AcpiLeaveSleepState(Sx) != AE_OK) {
+ PT(PT_LSS_FAIL);
+ PMD(PMD_SX, ("Problem with LeaveSleepState!\n"))
+ }
+
+ PT(PT_DPB);
+ if (AcpiDisableEvent(ACPI_EVENT_POWER_BUTTON, 0) != AE_OK) {
+ PT(PT_DPB_FAIL);
+ PMD(PMD_SX, ("Problem w/ DisableEvent(POWER_BUTTON)\n"))
+ }
+ if (acpi_rtc_wake > 0 &&
+ AcpiDisableEvent(ACPI_EVENT_RTC, 0) != AE_OK) {
+ PT(PT_DRTC_FAIL);
+ PMD(PMD_SX, ("Problem w/ DisableEvent(RTC)\n"))
+ }
+
+ PMD(PMD_SX, ("Restore state of APICs\n"))
+
+ /* Restore state of APICs */
+ PT(PT_ACPIREINIT);
+ reinit_picmode();
+ PT(PT_ACPIRESTORE);
+
+ PMD(PMD_SX, ("Exiting acpi_sleepstate() => 0\n"))
+
+ return (0);
+}
diff --git a/usr/src/uts/i86pc/io/psm/psm_common.c b/usr/src/uts/i86pc/io/psm/psm_common.c
index fd92327093..bfbcfcb1be 100644
--- a/usr/src/uts/i86pc/io/psm/psm_common.c
+++ b/usr/src/uts/i86pc/io/psm/psm_common.c
@@ -623,9 +623,9 @@ acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
}
intr_flagp->intr_el = psm_acpi_edgelevel(
- rp->Data.Irq.Triggering);
+ rp->Data.Irq.Triggering);
intr_flagp->intr_po = psm_acpi_po(
- rp->Data.Irq.Polarity);
+ rp->Data.Irq.Polarity);
irq = rp->Data.Irq.Interrupts[0];
status = ACPI_PSM_SUCCESS;
} else if (rp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
@@ -644,9 +644,9 @@ acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
}
intr_flagp->intr_el = psm_acpi_edgelevel(
- rp->Data.ExtendedIrq.Triggering);
+ rp->Data.ExtendedIrq.Triggering);
intr_flagp->intr_po = psm_acpi_po(
- rp->Data.ExtendedIrq.Polarity);
+ rp->Data.ExtendedIrq.Polarity);
irq = rp->Data.ExtendedIrq.Interrupts[0];
status = ACPI_PSM_SUCCESS;
}
@@ -799,7 +799,7 @@ acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
/* NEEDSWORK: move this into add_irqlist_entry someday */
irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist),
- KM_SLEEP);
+ KM_SLEEP);
for (i = 0; i < irqlist_len; i++)
if (resp->Type == ACPI_RESOURCE_TYPE_IRQ)
irqlist[i] = ((uint8_t *)tmplist)[i];
@@ -808,7 +808,7 @@ acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
intr_flags.intr_el = psm_acpi_edgelevel(el);
intr_flags.intr_po = psm_acpi_po(po);
acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len,
- &intr_flags);
+ &intr_flags);
}
AcpiOsFree(rsb.Pointer);
@@ -925,6 +925,32 @@ acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
return (ret);
}
+/*
+ * Walk the irq_cache_table and re-configure the link device to
+ * the saved state.
+ */
+void
+acpi_restore_link_devices(void)
+{
+ irq_cache_t *irqcachep;
+ acpi_psm_lnk_t psmlnk;
+ int i, status;
+
+ /* XXX: may not need to hold this mutex */
+ mutex_enter(&acpi_irq_cache_mutex);
+ for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
+ irqcachep++, i++) {
+ /* only field used from psmlnk in set_irq is lnkobj */
+ psmlnk.lnkobj = irqcachep->lnkobj;
+ status = acpi_set_irq_resource(&psmlnk, irqcachep->irq);
+ /* warn if set_irq failed; soldier on */
+ if (status != ACPI_PSM_SUCCESS)
+ cmn_err(CE_WARN, "restore_link failed for IRQ 0x%x\n",
+ irqcachep->irq);
+ }
+ mutex_exit(&acpi_irq_cache_mutex);
+}
+
int
acpi_poweroff(void)
{
diff --git a/usr/src/uts/i86pc/io/psm/uppc.c b/usr/src/uts/i86pc/io/psm/uppc.c
index 51181b0415..2fd3f860c5 100644
--- a/usr/src/uts/i86pc/io/psm/uppc.c
+++ b/usr/src/uts/i86pc/io/psm/uppc.c
@@ -25,7 +25,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/mutex.h>
#include <sys/types.h>
diff --git a/usr/src/uts/i86pc/io/todpc_subr.c b/usr/src/uts/i86pc/io/todpc_subr.c
index 7e55876af3..5b92cb9077 100644
--- a/usr/src/uts/i86pc/io/todpc_subr.c
+++ b/usr/src/uts/i86pc/io/todpc_subr.c
@@ -47,13 +47,79 @@
#include <sys/stat.h>
#include <sys/sunddi.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+
static int todpc_rtcget(unsigned char *buf);
static void todpc_rtcput(unsigned char *buf);
+#define CLOCK_RES 1000 /* 1 microsec in nanosecs */
+
+int clock_res = CLOCK_RES;
+
+/*
+ * The minimum sleep time till an alarm can be fired.
+ * This can be tuned in /etc/system, but if the value is too small,
+ * there is a danger that it will be missed if it takes too long to
+ * get from the set point to sleep. Or that it can fire quickly, and
+ * generate a power spike on the hardware. And small values are
+ * probably only usefull for test setups.
+ */
+int clock_min_alarm = 4;
+
/*
* Machine-dependent clock routines.
*/
+extern long gmt_lag;
+
+struct rtc_offset {
+ int8_t loaded;
+ uint8_t day_alrm;
+ uint8_t mon_alrm;
+ uint8_t century;
+};
+
+static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
+
+
+/*
+ * Entry point for ACPI to pass RTC or other clock values that
+ * are useful to TOD.
+ */
+void
+pc_tod_set_rtc_offsets(FADT_DESCRIPTOR *fadt) {
+ int ok = 0;
+
+ /*
+ * ASSERT is for debugging, but we don't want the machine
+ * falling over because for some reason we didn't get a valid
+ * pointer.
+ */
+ ASSERT(fadt);
+ if (fadt == NULL) {
+ return;
+ }
+
+ if (fadt->DayAlrm) {
+ pc_rtc_offset.day_alrm = fadt->DayAlrm;
+ ok = 1;
+ }
+
+ if (fadt->MonAlrm) {
+ pc_rtc_offset.mon_alrm = fadt->MonAlrm;
+ ok = 1;
+ }
+
+ if (fadt->Century) {
+ pc_rtc_offset.century = fadt->Century;
+ ok = 1;
+ }
+
+ pc_rtc_offset.loaded = ok;
+}
+
+
/*
* Write the specified time into the clock chip.
* Must be called with tod_lock held.
@@ -123,7 +189,7 @@ todpc_get(tod_ops_t *top)
if (tod.tod_year < 69) {
if (range_warn && tod.tod_year > 38) {
cmn_err(CE_WARN, "hardware real-time clock is out "
- "of range -- time needs to be reset");
+ "of range -- time needs to be reset");
range_warn = 0;
}
tod.tod_year += 100 + YRBASE; /* 20xx year */
@@ -135,12 +201,12 @@ todpc_get(tod_ops_t *top)
}
if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
cmn_err(CE_NOTE,
- "The hardware real-time clock appears to have the "
- "wrong century: %d.\nSolaris will still operate "
- "correctly, but other OS's/firmware agents may "
- "not.\nUse date(1) to set the date to the current "
- "time to correct the RTC.",
- BCD_TO_BYTE(rtc.rtc_century));
+ "The hardware real-time clock appears to have the "
+ "wrong century: %d.\nSolaris will still operate "
+ "correctly, but other OS's/firmware agents may "
+ "not.\nUse date(1) to set the date to the current "
+ "time to correct the RTC.",
+ BCD_TO_BYTE(rtc.rtc_century));
century_warn = 0;
}
tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon);
@@ -156,6 +222,109 @@ todpc_get(tod_ops_t *top)
return (ts);
}
+#include <sys/promif.h>
+/*
+ * Write the specified wakeup alarm into the clock chip.
+ * Must be called with tod_lock held.
+ */
+void
+/*ARGSUSED*/
+todpc_setalarm(tod_ops_t *top, int nsecs)
+{
+ struct rtc_t rtc;
+ int delta, asec, amin, ahr, adom, amon;
+ int day_alrm = pc_rtc_offset.day_alrm;
+ int mon_alrm = pc_rtc_offset.mon_alrm;
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ /* A delay of zero is not allowed */
+ if (nsecs == 0)
+ return;
+
+ /* Make sure that we delay no less than the minimum time */
+ if (nsecs < clock_min_alarm)
+ nsecs = clock_min_alarm;
+
+ if (todpc_rtcget((unsigned char *)&rtc))
+ return;
+
+ /*
+ * Compute alarm secs, mins and hrs, and where appropriate, dom
+ * and mon. rtc bytes are in binary-coded decimal, so we have
+ * to convert.
+ */
+ delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
+ asec = delta % 60;
+
+ delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
+ amin = delta % 60;
+
+ delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
+ ahr = delta % 24;
+
+ if (day_alrm == 0 && delta >= 24) {
+ prom_printf("No day alarm - set to end of today!\n");
+ asec = 59;
+ amin = 59;
+ ahr = 23;
+ } else {
+ int mon = BCD_TO_BYTE(rtc.rtc_mon);
+ static int dpm[] =
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
+
+ if (mon_alrm == 0) {
+ if (adom > dpm[mon]) {
+ prom_printf("No mon alarm - "
+ "set to end of current month!\n");
+ asec = 59;
+ amin = 59;
+ ahr = 23;
+ adom = dpm[mon];
+ }
+ } else {
+ for (amon = mon;
+ amon <= 12 && adom > dpm[amon]; amon++) {
+ adom -= dpm[amon];
+ }
+ if (amon > 12) {
+ prom_printf("Alarm too far in future - "
+ "set to end of current year!\n");
+ asec = 59;
+ amin = 59;
+ ahr = 23;
+ adom = dpm[12];
+ amon = 12;
+ }
+ rtc.rtc_amon = BYTE_TO_BCD(amon);
+ }
+
+ rtc.rtc_adom = BYTE_TO_BCD(adom);
+ }
+
+ rtc.rtc_asec = BYTE_TO_BCD(asec);
+ rtc.rtc_amin = BYTE_TO_BCD(amin);
+ rtc.rtc_ahr = BYTE_TO_BCD(ahr);
+
+ rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */
+
+ todpc_rtcput((unsigned char *)&rtc);
+}
+
+/*
+ * Clear an alarm. This is effectively setting an alarm of 0.
+ */
+void
+/*ARGSUSED*/
+todpc_clralarm(tod_ops_t *top)
+{
+ mutex_enter(&tod_lock);
+ todpc_setalarm(top, 0);
+ mutex_exit(&tod_lock);
+}
+
/*
* Routine to read contents of real time clock to the specified buffer.
* Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
@@ -176,9 +345,18 @@ todpc_rtcget(unsigned char *buf)
int i;
int retries = 256;
unsigned char *rawp;
+ unsigned char century = RTC_CENTURY;
+ unsigned char day_alrm;
+ unsigned char mon_alrm;
ASSERT(MUTEX_HELD(&tod_lock));
+ day_alrm = pc_rtc_offset.day_alrm;
+ mon_alrm = pc_rtc_offset.mon_alrm;
+ if (pc_rtc_offset.century != 0) {
+ century = pc_rtc_offset.century;
+ }
+
outb(RTC_ADDR, RTC_D); /* check if clock valid */
reg = inb(RTC_DATA);
if ((reg & RTC_VRT) == 0)
@@ -198,8 +376,18 @@ checkuip:
outb(RTC_ADDR, i);
*rawp++ = inb(RTC_DATA);
}
- outb(RTC_ADDR, RTC_CENTURY); /* do century */
+ outb(RTC_ADDR, century); /* do century */
((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
+
+ if (day_alrm > 0) {
+ outb(RTC_ADDR, day_alrm);
+ ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
+ }
+ if (mon_alrm > 0) {
+ outb(RTC_ADDR, mon_alrm);
+ ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
+ }
+
outb(RTC_ADDR, 0); /* re-read Seconds register */
reg = inb(RTC_DATA);
if (reg != ((struct rtc_t *)buf)->rtc_sec ||
@@ -221,6 +409,13 @@ todpc_rtcput(unsigned char *buf)
{
unsigned char reg;
int i;
+ unsigned char century = RTC_CENTURY;
+ unsigned char day_alrm = pc_rtc_offset.day_alrm;
+ unsigned char mon_alrm = pc_rtc_offset.mon_alrm;
+
+ if (pc_rtc_offset.century != 0) {
+ century = pc_rtc_offset.century;
+ }
outb(RTC_ADDR, RTC_B);
reg = inb(RTC_DATA);
@@ -230,8 +425,20 @@ todpc_rtcput(unsigned char *buf)
outb(RTC_ADDR, i);
outb(RTC_DATA, buf[i]);
}
- outb(RTC_ADDR, RTC_CENTURY); /* do century */
+ outb(RTC_ADDR, century); /* do century */
outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
+
+ if (day_alrm > 0) {
+ outb(RTC_ADDR, day_alrm);
+ outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_adom);
+ }
+ if (mon_alrm > 0) {
+ outb(RTC_ADDR, mon_alrm);
+ outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
+ }
+
+ outb(RTC_ADDR, RTC_B);
+ reg = inb(RTC_DATA);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */
}
@@ -240,6 +447,10 @@ static tod_ops_t todpc_ops = {
TOD_OPS_VERSION,
todpc_get,
todpc_set,
+ NULL,
+ NULL,
+ todpc_setalarm,
+ todpc_clralarm,
NULL
};
diff --git a/usr/src/uts/i86pc/ml/cpr_wakecode.s b/usr/src/uts/i86pc/ml/cpr_wakecode.s
new file mode 100644
index 0000000000..93dffd9c51
--- /dev/null
+++ b/usr/src/uts/i86pc/ml/cpr_wakecode.s
@@ -0,0 +1,1033 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/asm_linkage.h>
+#include <sys/asm_misc.h>
+#include <sys/regset.h>
+#include <sys/privregs.h>
+#include <sys/x86_archext.h>
+#include <sys/cpr_wakecode.h>
+
+#if !defined(__lint)
+#include <sys/segments.h>
+#include "assym.h"
+#endif
+
+/*
+ * This file contains the low level routines involved in getting
+ * into and out of ACPI S3, including those needed for restarting
+ * the non-boot cpus.
+ *
+ * Our assumptions:
+ *
+ * Our actions:
+ *
+ */
+
+#if defined(lint) || defined(__lint)
+
+/*ARGSUSED*/
+int
+wc_save_context(wc_cpu_t *pcpu)
+{ return 0; }
+
+#else /* lint */
+
+#if defined(__GNU_AS__)
+
+ NOTHING AT ALL YET!
+
+#else /* !defined(__GNU_AS__) */
+
+#if defined(__amd64)
+
+ ENTRY_NP(wc_save_context)
+
+ movq (%rsp), %rdx / return address
+ movq %rdx, WC_RETADDR(%rdi)
+
+/*
+ * C calling convention with no local variables, just use 1st arg ie %rdi
+ * and the registers. Therefore push caller's fp, set out fp to be sp and
+ * push %r12, %r13 %r14. At function end unwind this by: popping %r14, %r13
+ * %r14, restore the sp from fp and pop caller's fp.
+ */
+
+ pushq %rbp
+ movq %rsp,%rbp
+ pushq %r12
+ pushq %r13
+ pushq %r14
+
+ movq %rdi, WC_VIRTADDR(%rdi)
+ movq %rdi, WC_RDI(%rdi)
+
+ movq %rdx, WC_RDX(%rdi)
+
+/ stash everything else we need
+ sgdt WC_GDT(%rdi)
+ sidt WC_IDT(%rdi)
+ sldt WC_LDT(%rdi)
+ str WC_TR(%rdi)
+
+ movq %cr0, %rdx
+ movq %rdx, WC_CR0(%rdi)
+ movq %cr3, %rdx
+ movq %rdx, WC_CR3(%rdi)
+ movq %cr4, %rdx
+ movq %rdx, WC_CR4(%rdi)
+ movq %cr8, %rdx
+ movq %rdx, WC_CR8(%rdi)
+
+ movq %r8, WC_R8(%rdi)
+ movq %r9, WC_R9(%rdi)
+ movq %r10, WC_R10(%rdi)
+ movq %r11, WC_R11(%rdi)
+ movq %r12, WC_R12(%rdi)
+ movq %r13, WC_R13(%rdi)
+ movq %r14, WC_R14(%rdi)
+ movq %r15, WC_R15(%rdi)
+ movq %rax, WC_RAX(%rdi)
+ movq %rbp, WC_RBP(%rdi)
+ movq %rbx, WC_RBX(%rdi)
+ movq %rcx, WC_RCX(%rdi)
+ movq %rsi, WC_RSI(%rdi)
+ movq %rsp, WC_RSP(%rdi)
+
+ movw %ss, WC_SS(%rdi)
+ movw %cs, WC_CS(%rdi)
+ movw %ds, WC_DS(%rdi)
+ movw %es, WC_ES(%rdi)
+
+ movq $0, %rcx / save %fs register
+ movw %fs, %cx
+ movq %rcx, WC_FS(%rdi)
+
+ movl $MSR_AMD_FSBASE, %ecx
+ rdmsr
+ movl %eax, WC_FSBASE(%rdi)
+ movl %edx, WC_FSBASE+4(%rdi)
+
+ movq $0, %rcx / save %gs register
+ movw %gs, %cx
+ movq %rcx, WC_GS(%rdi)
+
+ movl $MSR_AMD_GSBASE, %ecx / save gsbase msr
+ rdmsr
+ movl %eax, WC_GSBASE(%rdi)
+ movl %edx, WC_GSBASE+4(%rdi)
+
+ movl $MSR_AMD_KGSBASE, %ecx / save kgsbase msr
+ rdmsr
+ movl %eax, WC_KGSBASE(%rdi)
+ movl %edx, WC_KGSBASE+4(%rdi)
+
+ pushfq
+ popq WC_EFLAGS(%rdi)
+
+/*
+ * call save_stack(cpup)
+ * NB %rdi is the first arguemnt to both wc_save_context() and save_stack()
+ * so it must not be modified during either of these calls.
+ * The pushq will decrement the value of %rsp
+ * we need to push the %rbp because it is the frame pointer and we need
+ * to use the C calling convention
+ */
+
+ pushq %rbp
+ call *save_stack_func
+ popq %rbp
+
+ wbinvd / flush the cache
+
+ movq $1, %rax / at suspend return 1
+
+/ see comment at function enter
+ popq %r14
+ popq %r13
+ popq %r12
+ leave
+
+ ret
+
+ SET_SIZE(wc_save_context)
+
+#elif defined(__i386)
+
+ ENTRY_NP(wc_save_context)
+
+ movl 4(%esp), %eax / wc_cpu_t *
+ movl %eax, WC_VIRTADDR(%eax)
+
+ movl (%esp), %edx / return address
+ movl %edx, WC_RETADDR(%eax)
+
+ str WC_TR(%eax) / stash everything else we need
+ sgdt WC_GDT(%eax)
+ sldt WC_LDT(%eax)
+ sidt WC_IDT(%eax)
+
+ movl %cr0, %edx
+ movl %edx, WC_CR0(%eax)
+ movl %cr3, %edx
+ movl %edx, WC_CR3(%eax)
+ movl %cr4, %edx
+ movl %edx, WC_CR4(%eax)
+
+ movl %ebx, WC_EBX(%eax)
+ movl %edi, WC_EDI(%eax)
+ movl %esi, WC_ESI(%eax)
+ movl %ebp, WC_EBP(%eax)
+ movl %esp, WC_ESP(%eax)
+
+ movw %ss, WC_SS(%eax)
+ movw %cs, WC_CS(%eax)
+ movw %ds, WC_DS(%eax)
+ movw %es, WC_ES(%eax)
+ movw %fs, WC_FS(%eax)
+ movw %gs, WC_GS(%eax)
+
+ pushfl
+ popl WC_EFLAGS(%eax)
+
+ wbinvd / flush the cache
+
+ movl $1, %eax / at suspend return 1
+ ret
+
+ SET_SIZE(wc_save_context)
+
+#endif /* __amd64 */
+
+#endif /* __GNU_AS__ */
+
+#endif /* lint */
+
+
+/*
+ * Our assumptions:
+ * - We are running in real mode.
+ * - Interrupts are disabled.
+ *
+ * Our actions:
+ * - We start using our GDT by loading correct values in the
+ * selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL,
+ * gs=KGS_SEL).
+ * - We change over to using our IDT.
+ * - We load the default LDT into the hardware LDT register.
+ * - We load the default TSS into the hardware task register.
+ * - We restore registers
+ * - We return to original caller (a la setjmp)
+ */
+
+#if defined(lint) || defined(__lint)
+
+void
+wc_rm_start(void)
+{}
+
+void
+wc_rm_end(void)
+{}
+
+#else /* lint */
+
+#if defined(__GNU_AS__)
+
+ NOTHING AT ALL YET!
+
+#else /* __GNU_AS__ */
+
+#if defined(__amd64)
+
+ ENTRY_NP(wc_rm_start)
+
+ /*
+ * For vulcan as we need to do a .code32 and mentally invert the
+ * meaning of the addr16 and data16 prefixes to get 32-bit access when
+ * generating code to be executed in 16-bit mode (sigh...)
+ */
+
+ .code32
+
+ cli
+ movw %cs, %ax
+ movw %ax, %ds / establish ds ...
+ movw %ax, %ss / ... and ss:esp
+ D16 movl $WC_STKSTART, %esp
+/ using the following value blows up machines! - DO NOT USE
+/ D16 movl 0xffc, %esp
+
+#define LED 1
+#define SERIAL 1
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd1, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x61, %al
+ outb (%dx)
+#endif
+
+ D16 call cominit
+
+ /*
+ * Enable protected-mode, write protect, and alignment mask
+ * %cr0 has already been initialsed to zero
+ */
+ movl %cr0, %eax
+ D16 orl $[CR0_PE|CR0_WP|CR0_AM], %eax
+ movl %eax, %cr0
+
+ /*
+ * Do a jmp immediately after writing to cr0 when enabling protected
+ * mode to clear the real mode prefetch queue (per Intel's docs)
+ */
+ jmp pestart
+pestart:
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd2, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x62, %al
+ outb (%dx)
+#endif
+
+ /*
+ * 16-bit protected mode is now active, so prepare to turn on long
+ * mode
+ */
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd3, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x63, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Add any initial cr4 bits
+ */
+ movl %cr4, %eax
+ A16 D16 orl CR4OFF, %eax
+
+ /*
+ * Enable PAE mode (CR4.PAE)
+ */
+ D16 orl $CR4_PAE, %eax
+ movl %eax, %cr4
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd4, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x64, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Point cr3 to the 64-bit long mode page tables.
+ *
+ * Note that these MUST exist in 32-bit space, as we don't have
+ * a way to load %cr3 with a 64-bit base address for the page tables
+ * until the CPU is actually executing in 64-bit long mode.
+ */
+ A16 D16 movl CR3OFF, %eax
+ movl %eax, %cr3
+
+ /*
+ * Set long mode enable in EFER (EFER.LME = 1)
+ */
+ D16 movl $MSR_AMD_EFER, %ecx
+ rdmsr
+
+ D16 orl $AMD_EFER_LME, %eax
+ wrmsr
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd5, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x65, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Finally, turn on paging (CR0.PG = 1) to activate long mode.
+ */
+ movl %cr0, %eax
+ D16 orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /*
+ * The instruction after enabling paging in CR0 MUST be a branch.
+ */
+ jmp long_mode_active
+
+long_mode_active:
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd6, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x66, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Long mode is now active but since we're still running with the
+ * original 16-bit CS we're actually in 16-bit compatability mode.
+ *
+ * We have to load an intermediate GDT and IDT here that we know are
+ * in 32-bit space before we can use the kernel's GDT and IDT, which
+ * may be in the 64-bit address space, and since we're in compatability
+ * mode, we only have access to 16 and 32-bit instructions at the
+ * moment.
+ */
+ A16 D16 lgdt TEMPGDTOFF /* load temporary GDT */
+ A16 D16 lidt TEMPIDTOFF /* load temporary IDT */
+
+
+ /*
+ * Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit
+ * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump
+ * to the real mode platter address of wc_long_mode_64 as until the
+ * 64-bit CS is in place we don't have access to 64-bit instructions
+ * and thus can't reference a 64-bit %rip.
+ */
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd7, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x67, %al
+ outb (%dx)
+#endif
+
+ D16 pushl $TEMP_CS64_SEL
+ A16 D16 pushl LM64OFF
+
+ D16 lret
+
+
+/*
+ * Support routine to re-initialize VGA subsystem
+ */
+vgainit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize keyboard (which is USB - help!)
+ */
+kbdinit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize COM ports to something sane
+ */
+cominit:
+ / init COM1 & COM2
+ xorl %edx, %edx / select COM1
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 movl $1, %edx / select COM2
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 ret
+
+ .code64
+/*
+ * Support routine to re-initialize COM ports to something sane
+ */
+cominit64:
+ / init COM1 & COM2
+ xorq %rdx, %rdx / select COM1
+ movq $0xe3, %rax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ movq $1, %rdx / select COM2
+ movq $0xe3, %rax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ ret
+
+ .globl wc_long_mode_64
+wc_long_mode_64:
+
+#if LED
+ movw $0x80, %dx
+ movb $0xd8, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x68, %al
+ outb (%dx)
+#endif
+
+ /*
+ * We are now running in long mode with a 64-bit CS (EFER.LMA=1,
+ * CS.L=1) so we now have access to 64-bit instructions.
+ *
+ * First, set the 64-bit GDT base.
+ */
+ .globl rm_platter_pa
+ movl rm_platter_pa, %eax
+
+ lgdtq GDTROFF(%rax) /* load 64-bit GDT */
+
+ /*
+ * Save the CPU number in %r11; get the value here since it's saved in
+ * the real mode platter.
+ */
+/ JAN
+/ the following is wrong! need to figure out MP systems
+/ movl CPUNOFF(%rax), %r11d
+
+ /*
+ * Add rm_platter_pa to %rsp to point it to the same location as seen
+ * from 64-bit mode.
+ */
+ addq %rax, %rsp
+
+ /*
+ * Now do an lretq to load CS with the appropriate selector for the
+ * kernel's 64-bit GDT and to start executing 64-bit setup code at the
+ * virtual address where boot originally loaded this code rather than
+ * the copy in the real mode platter's rm_code array as we've been
+ * doing so far.
+ */
+
+#if LED
+ movw $0x80, %dx
+ movb $0xd9, %al
+ outb (%dx)
+#endif
+
+/ JAN this should produce 'i' but we get 'g' instead ???
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x69, %al
+ outb (%dx)
+#endif
+
+ pushq $KCS_SEL
+ pushq $kernel_wc_code
+ lretq
+
+ .globl kernel_wc_code
+kernel_wc_code:
+
+#if LED
+ movw $0x80, %dx
+ movb $0xda, %al
+ outb (%dx)
+#endif
+
+/ JAN this should produce 'j' but we get 'g' instead ???
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x6a, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Complete the balance of the setup we need to before executing
+ * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS).
+ */
+ .globl rm_platter_va
+ movq rm_platter_va, %rbx
+ addq $WC_CPU, %rbx
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdb, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movw $0x6b, %ax
+ outb (%dx)
+#endif
+
+ /*
+ * restore the rest of the registers
+ */
+
+ lidtq WC_IDT(%rbx)
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdc, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movw $0x6c, %ax
+ outb (%dx)
+#endif
+
+ /*
+ * restore the rest of the registers
+ */
+
+ movw $KDS_SEL, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+
+ /*
+ * Before proceeding, enable usage of the page table NX bit if
+ * that's how the page tables are set up.
+ */
+ movl x86_feature, %ecx
+ andl $X86_NX, %ecx
+ jz 1f
+ movl $MSR_AMD_EFER, %ecx
+ rdmsr
+ orl $AMD_EFER_NXE, %eax
+ wrmsr
+1:
+
+ movq WC_CR4(%rbx), %rax / restore full cr4 (with Global Enable)
+ movq %rax, %cr4
+
+ lldt WC_LDT(%rbx)
+ movzwq WC_TR(%rbx), %rax / clear TSS busy bit
+ addq WC_GDT+2(%rbx), %rax
+ andl $0xfffffdff, 4(%rax)
+ movq 4(%rax), %rcx
+ ltr WC_TR(%rbx)
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdd, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movw $0x6d, %ax
+ outb (%dx)
+#endif
+
+/ restore %fsbase %gsbase %kgbase registers using wrmsr instruction
+
+ movq WC_FS(%rbx), %rcx / restore fs register
+ movw %cx, %fs
+
+ movl $MSR_AMD_FSBASE, %ecx
+ movl WC_FSBASE(%rbx), %eax
+ movl WC_FSBASE+4(%rbx), %edx
+ wrmsr
+
+ movq WC_GS(%rbx), %rcx / restore gs register
+ movw %cx, %gs
+
+ movl $MSR_AMD_GSBASE, %ecx / restore gsbase msr
+ movl WC_GSBASE(%rbx), %eax
+ movl WC_GSBASE+4(%rbx), %edx
+ wrmsr
+
+ movl $MSR_AMD_KGSBASE, %ecx / restore kgsbase msr
+ movl WC_KGSBASE(%rbx), %eax
+ movl WC_KGSBASE+4(%rbx), %edx
+ wrmsr
+
+ movq WC_CR0(%rbx), %rdx
+ movq %rdx, %cr0
+ movq WC_CR3(%rbx), %rdx
+ movq %rdx, %cr3
+ movq WC_CR8(%rbx), %rdx
+ movq %rdx, %cr8
+
+#if LED
+ movw $0x80, %dx
+ movb $0xde, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x6e, %al
+ outb (%dx)
+#endif
+
+/ dummy up a stck so we can make C function calls
+ movq WC_RSP(%rbx), %rsp
+
+ /*
+ * APIC initialization (we dummy up a stack so we can make this call)
+ */
+ pushq $0 /* null frame pointer terminates stack trace */
+ movq %rsp, %rbp /* stack aligned on 16-byte boundary */
+
+ call *ap_mlsetup
+
+ call *cpr_start_cpu_func
+
+/ restore %rbx to the value it ahd before we called the functions above
+ movq rm_platter_va, %rbx
+ addq $WC_CPU, %rbx
+
+ movq WC_R8(%rbx), %r8
+ movq WC_R9(%rbx), %r9
+ movq WC_R10(%rbx), %r10
+ movq WC_R11(%rbx), %r11
+ movq WC_R12(%rbx), %r12
+ movq WC_R13(%rbx), %r13
+ movq WC_R14(%rbx), %r14
+ movq WC_R15(%rbx), %r15
+/ movq WC_RAX(%rbx), %rax
+ movq WC_RBP(%rbx), %rbp
+ movq WC_RCX(%rbx), %rcx
+/ movq WC_RDX(%rbx), %rdx
+ movq WC_RDI(%rbx), %rdi
+ movq WC_RSI(%rbx), %rsi
+
+
+/ assume that %cs does not need to be restored
+/ %ds, %es & %ss are ignored in 64bit mode
+ movw WC_SS(%rbx), %ss
+ movw WC_DS(%rbx), %ds
+ movw WC_ES(%rbx), %es
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdf, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x6f, %al
+ outb (%dx)
+#endif
+
+
+ movq WC_RBP(%rbx), %rbp
+ movq WC_RSP(%rbx), %rsp
+
+#if LED
+ movw $0x80, %dx
+ movb $0xe0, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x70, %al
+ outb (%dx)
+#endif
+
+
+ movq WC_RCX(%rbx), %rcx
+
+ pushq WC_EFLAGS(%rbx) / restore flags
+ popfq
+
+#if LED
+ movw $0x80, %dx
+ movb $0xe1, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x71, %al
+ outb (%dx)
+#endif
+
+/*
+ * can not use outb after this point, because doing so would mean using
+ * %dx which would modify %rdx which is restored here
+ */
+
+ movq %rbx, %rax
+ movq WC_RDX(%rax), %rdx
+ movq WC_RBX(%rax), %rbx
+
+ popq %r14
+ popq %r13
+ popq %r12
+ leave
+
+ movq WC_RETADDR(%rax), %rax
+ movq %rax, (%rsp) / return to caller of wc_save_context
+
+ xorl %eax, %eax / at wakeup return 0
+ ret
+
+
+ SET_SIZE(wc_rm_start)
+
+ ENTRY_NP(asmspin)
+
+ movl %edi, %ecx
+A1:
+ loop A1
+
+ SET_SIZE(asmspin)
+
+ .globl wc_rm_end
+wc_rm_end:
+ nop
+
+#elif defined(__i386)
+
+ ENTRY_NP(wc_rm_start)
+
+/entry: jmp entry / stop here for HDT
+
+ cli
+ movw %cs, %ax
+ movw %ax, %ds / establish ds ...
+ movw %ax, %ss / ... and ss:esp
+ D16 movl $WC_STKSTART, %esp
+
+#define LED 1
+#define SERIAL 1
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd1, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x61, %al
+ outb (%dx)
+#endif
+
+
+ D16 call vgainit
+ D16 call kbdinit
+ D16 call cominit
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd2, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x62, %al
+ outb (%dx)
+#endif
+
+ D16 A16 movl $WC_CPU, %ebx / base add of wc_cpu_t
+
+#if LED
+ D16 movb $0xd3, %al
+ outb $0x80
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x63, %al
+ outb (%dx)
+#endif
+
+ D16 A16 movl %cs:WC_DS(%ebx), %edx / %ds post prot/paging transit
+
+ D16 movb $0xd4, %al
+ outb $0x80
+
+ D16 A16 lgdt %cs:WC_GDT(%ebx) / restore gdt and idtr
+ D16 A16 lidt %cs:WC_IDT(%ebx)
+
+ D16 movb $0xd5, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_CR4(%ebx), %eax / restore cr4
+ D16 andl $-1!CR4_PGE, %eax / don't set Global Enable yet
+ movl %eax, %cr4
+
+ D16 movb $0xd6, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_CR3(%ebx), %eax / set PDPT
+ movl %eax, %cr3
+
+ D16 movb $0xd7, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_CR0(%ebx), %eax / enable prot/paging, etc.
+ movl %eax, %cr0
+
+ D16 movb $0xd8, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_VIRTADDR(%ebx), %ebx / virtaddr of wc_cpu_t
+
+ D16 movb $0xd9, %al
+ outb $0x80
+
+ D16 movb $0xda, %al
+ outb $0x80
+ jmp flush / flush prefetch queue
+flush:
+ D16 pushl $KCS_SEL
+ D16 pushl $kernel_wc_code
+ D16 lret / re-appear at kernel_wc_code
+
+
+/*
+ * Support routine to re-initialize VGA subsystem
+ */
+vgainit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize keyboard (which is USB - help!)
+ */
+kbdinit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize COM ports to something sane for debug output
+ */
+cominit:
+ / init COM1 & COM2
+ xorl %edx, %edx / select COM1
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 movl $1, %edx / select COM2
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 ret
+
+ .globl wc_rm_end
+wc_rm_end:
+ nop
+
+ .globl kernel_wc_code
+kernel_wc_code:
+ / At this point we are with kernel's cs and proper eip.
+ / We will be executing not from the copy in real mode platter,
+ / but from the original code where boot loaded us.
+ / By this time GDT and IDT are loaded as is cr0, cr3 and cr4.
+ / %ebx is wc_cpu
+ / %dx is our ds
+
+ D16 movb $0xdb, %al
+ outb $0x80
+
+/ got here OK
+
+ movw %dx, %ds / $KDS_SEL
+ movb $0xdc, %al
+ outb $0x80
+
+ movl $MSR_AMD_EFER, %ecx / re-enable NX bit
+ rdmsr
+ orl $AMD_EFER_NXE, %eax
+ wrmsr
+
+ movl WC_CR4(%ebx), %eax / restore full cr4 (with Global Enable)
+ movl %eax, %cr4
+
+
+ lldt WC_LDT(%ebx) / $LDT_SEL
+
+ movzwl WC_TR(%ebx), %eax / clear TSS busy bit
+ addl WC_GDT+2(%ebx), %eax
+ andl $-1!0x200, 4(%eax)
+ ltr WC_TR(%ebx) / $UTSS_SEL
+
+ movw WC_SS(%ebx), %ss / lssl WC_ESP(%ebx), %esp
+ movl WC_ESP(%ebx), %esp / ^ don't use, asm busted!
+
+ movl WC_RETADDR(%ebx), %eax / return to caller of wc_save_context
+ movl %eax, (%esp)
+
+ movw WC_ES(%ebx), %es / restore segment registers
+ movw WC_FS(%ebx), %fs
+ movw WC_GS(%ebx), %gs
+
+ /*
+ * APIC initialization
+ */
+ call *ap_mlsetup
+
+ call *cpr_start_cpu_func
+
+ pushl WC_EFLAGS(%ebx) / restore flags
+ popfl
+
+ movl WC_EDI(%ebx), %edi / restore general registers
+ movl WC_ESI(%ebx), %esi
+ movl WC_EBP(%ebx), %ebp
+ movl WC_EBX(%ebx), %ebx
+
+/exit: jmp exit / stop here for HDT
+
+ xorl %eax, %eax / at wakeup return 0
+ ret
+
+ SET_SIZE(wc_rm_start)
+
+
+#endif /* defined(__amd64) */
+
+#endif /* !defined(__GNU_AS__) */
+
+#endif /* lint */
+
diff --git a/usr/src/uts/i86pc/ml/offsets.in b/usr/src/uts/i86pc/ml/offsets.in
index 7219b7a02f..eff25dbcc2 100644
--- a/usr/src/uts/i86pc/ml/offsets.in
+++ b/usr/src/uts/i86pc/ml/offsets.in
@@ -60,6 +60,7 @@
#include <sys/lgrp.h>
#include <sys/dtrace.h>
#include <sys/brand.h>
+#include <sys/cpr_wakecode.h>
proc PROCSIZE
p_link
@@ -387,3 +388,52 @@ brand
zone
zone_brand_data
+
+wc_cpu WC_CPU_SIZE
+ wc_retaddr
+ wc_virtaddr
+ wc_cr0
+ wc_cr3
+ wc_cr4
+ wc_cr8
+ wc_fs
+ wc_fsbase
+ wc_gs
+ wc_gsbase
+ wc_kgsbase
+ wc_r8
+ wc_r9
+ wc_r10
+ wc_r11
+ wc_r12
+ wc_r13
+ wc_r14
+ wc_r15
+ wc_rax
+ wc_rbp
+ wc_rbx
+ wc_rcx
+ wc_rdi
+ wc_rdx
+ wc_rsi
+ wc_rsp
+ wc_gdt_limit WC_GDT
+ wc_gdt_base
+ wc_idt_limit WC_IDT
+ wc_idt_base
+ wc_tr
+ wc_ldt
+ wc_eflags
+ wc_ebx
+ wc_edi
+ wc_esi
+ wc_ebp
+ wc_esp
+ wc_esp
+ wc_ss
+ wc_cs
+ wc_ds
+ wc_es
+
+wc_wakecode
+ wc_cpu
diff --git a/usr/src/uts/i86pc/os/acpi_stubs.c b/usr/src/uts/i86pc/os/acpi_stubs.c
new file mode 100644
index 0000000000..5f3f71556f
--- /dev/null
+++ b/usr/src/uts/i86pc/os/acpi_stubs.c
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+
+/*
+ * This file contains ACPI functions that are needed by the kernel before
+ * the ACPI module is loaded. Any functions or definitions need to be
+ * able to deal with the possibility that ACPI doesn't get loaded, or
+ * doesn't contain the required method.
+ */
+
+int (*acpi_fp_setwake)();
+
+/*
+ *
+ */
+int
+acpi_ddi_setwake(dev_info_t *dip, int level)
+{
+ if (acpi_fp_setwake == NULL)
+ return (AE_ERROR);
+
+ return ((*acpi_fp_setwake)(dip, level));
+}
diff --git a/usr/src/uts/i86pc/os/cpr_impl.c b/usr/src/uts/i86pc/os/cpr_impl.c
new file mode 100644
index 0000000000..939df37716
--- /dev/null
+++ b/usr/src/uts/i86pc/os/cpr_impl.c
@@ -0,0 +1,1096 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Platform specific implementation code
+ * Currently only suspend to RAM is supported (ACPI S3)
+ */
+
+#define SUNDDI_IMPL
+
+#include <sys/types.h>
+#include <sys/promif.h>
+#include <sys/prom_isa.h>
+#include <sys/prom_plat.h>
+#include <sys/cpuvar.h>
+#include <sys/pte.h>
+#include <vm/hat.h>
+#include <vm/page.h>
+#include <vm/as.h>
+#include <sys/cpr.h>
+#include <sys/kmem.h>
+#include <sys/clock.h>
+#include <sys/kmem.h>
+#include <sys/panic.h>
+#include <vm/seg_kmem.h>
+#include <sys/cpu_module.h>
+#include <sys/callb.h>
+#include <sys/machsystm.h>
+#include <sys/vmsystm.h>
+#include <sys/systm.h>
+#include <sys/archsystm.h>
+#include <sys/stack.h>
+#include <sys/fs/ufs_fs.h>
+#include <sys/memlist.h>
+#include <sys/bootconf.h>
+#include <sys/thread.h>
+#include <sys/x_call.h>
+#include <sys/smp_impldefs.h>
+#include <vm/vm_dep.h>
+#include <sys/psm.h>
+#include <sys/epm.h>
+#include <sys/cpr_wakecode.h>
+#include <sys/x86_archext.h>
+#include <sys/reboot.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+
+#define AFMT "%lx"
+
+extern int flushes_require_xcalls;
+extern cpuset_t cpu_ready_set;
+
+#if defined(__amd64)
+extern void *wc_long_mode_64(void);
+#endif /* __amd64 */
+extern int tsc_gethrtime_enable;
+extern void i_cpr_start_cpu(void);
+
+ushort_t cpr_mach_type = CPR_MACHTYPE_X86;
+void (*cpr_start_cpu_func)(void) = i_cpr_start_cpu;
+
+static wc_cpu_t *wc_other_cpus = NULL;
+static cpuset_t procset = 1;
+
+static void
+init_real_mode_platter(int cpun, uint32_t offset, uint_t cr4, wc_desctbr_t gdt);
+
+static int i_cpr_platform_alloc(psm_state_request_t *req);
+static void i_cpr_platform_free(psm_state_request_t *req);
+static int i_cpr_save_apic(psm_state_request_t *req);
+static int i_cpr_restore_apic(psm_state_request_t *req);
+
+#if defined(__amd64)
+static void restore_stack(wc_cpu_t *cpup);
+static void save_stack(wc_cpu_t *cpup);
+void (*save_stack_func)(wc_cpu_t *) = save_stack;
+#endif /* __amd64 */
+
+/*
+ * restart paused slave cpus
+ */
+void
+i_cpr_machdep_setup(void)
+{
+ if (ncpus > 1) {
+ CPR_DEBUG(CPR_DEBUG1, ("MP restarted...\n"));
+ mutex_enter(&cpu_lock);
+ start_cpus();
+ mutex_exit(&cpu_lock);
+ }
+}
+
+
+/*
+ * Stop all interrupt activities in the system
+ */
+void
+i_cpr_stop_intr(void)
+{
+ (void) spl7();
+}
+
+/*
+ * Set machine up to take interrupts
+ */
+void
+i_cpr_enable_intr(void)
+{
+ (void) spl0();
+}
+
+/*
+ * Save miscellaneous information which needs to be written to the
+ * state file. This information is required to re-initialize
+ * kernel/prom handshaking.
+ */
+void
+i_cpr_save_machdep_info(void)
+{
+ int notcalled = 0;
+ ASSERT(notcalled);
+}
+
+
+void
+i_cpr_set_tbr(void)
+{
+}
+
+
+processorid_t
+i_cpr_bootcpuid(void)
+{
+ return (0);
+}
+
+/*
+ * cpu0 should contain bootcpu info
+ */
+cpu_t *
+i_cpr_bootcpu(void)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ return (cpu_get(i_cpr_bootcpuid()));
+}
+
+/*
+ * Save context for the specified CPU
+ */
+void *
+i_cpr_save_context(void *arg)
+{
+ long index = (long)arg;
+ psm_state_request_t *papic_state;
+ int resuming;
+ int ret;
+
+ PMD(PMD_SX, ("i_cpr_save_context() index = %ld\n", index))
+
+ ASSERT(index < NCPU);
+
+ papic_state = &(wc_other_cpus + index)->wc_apic_state;
+
+ ret = i_cpr_platform_alloc(papic_state);
+ ASSERT(ret == 0);
+
+ ret = i_cpr_save_apic(papic_state);
+ ASSERT(ret == 0);
+
+ /*
+ * wc_save_context returns twice, once when susending and
+ * once when resuming, wc_save_context() returns 0 when
+ * suspending and non-zero upon resume
+ */
+ resuming = (wc_save_context(wc_other_cpus + index) == 0);
+
+ PMD(PMD_SX, ("i_cpr_save_context: wc_save_context returns %d\n",
+ resuming))
+
+ /*
+ * do NOT call any functions after this point, because doing so
+ * will modify the stack that we are running on
+ */
+
+ if (resuming) {
+
+ ret = i_cpr_restore_apic(papic_state);
+ ASSERT(ret == 0);
+
+ i_cpr_platform_free(papic_state);
+
+ /*
+ * Setting the bit in cpu_ready_set must be the last operation
+ * in processor initialization; the boot CPU will continue to
+ * boot once it sees this bit set for all active CPUs.
+ */
+ CPUSET_ATOMIC_ADD(cpu_ready_set, CPU->cpu_id);
+
+ PMD(PMD_SX,
+ ("cpu_release() cpu_ready_set = %lx, CPU->cpu_id = %d\n",
+ cpu_ready_set, CPU->cpu_id))
+ }
+ return (NULL);
+}
+
+static ushort_t *warm_reset_vector = NULL;
+
+static ushort_t *
+map_warm_reset_vector()
+{
+ /*LINTED*/
+ if (!(warm_reset_vector = (ushort_t *)psm_map_phys(WARM_RESET_VECTOR,
+ sizeof (ushort_t *), PROT_READ|PROT_WRITE)))
+ return (NULL);
+
+ /*
+ * setup secondary cpu bios boot up vector
+ */
+ *warm_reset_vector = (ushort_t)((caddr_t)
+ /*LINTED*/
+ ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
+ + ((ulong_t)rm_platter_va & 0xf));
+ warm_reset_vector++;
+ *warm_reset_vector = (ushort_t)(rm_platter_pa >> 4);
+
+ --warm_reset_vector;
+ return (warm_reset_vector);
+}
+
+void
+i_cpr_pre_resume_cpus()
+{
+ /*
+ * this is a cut down version of start_other_cpus()
+ * just do the initialization to wake the other cpus
+ */
+ unsigned who;
+ int cpuid = i_cpr_bootcpuid();
+ int started_cpu;
+ uint32_t code_length = 0;
+ caddr_t wakevirt = rm_platter_va;
+ /*LINTED*/
+ wakecode_t *wp = (wakecode_t *)wakevirt;
+ char *str = "i_cpr_pre_resume_cpus";
+ extern int get_tsc_ready();
+ int err;
+
+ /*LINTED*/
+ rm_platter_t *real_mode_platter = (rm_platter_t *)rm_platter_va;
+
+ /*
+ * Copy the real mode code at "real_mode_start" to the
+ * page at rm_platter_va.
+ */
+ warm_reset_vector = map_warm_reset_vector();
+ if (warm_reset_vector == NULL) {
+ PMD(PMD_SX, ("i_cpr_pre_resume_cpus() returning #2\n"))
+ return;
+ }
+
+ flushes_require_xcalls = 1;
+
+ /*
+ * We lock our affinity to the master CPU to ensure that all slave CPUs
+ * do their TSC syncs with the same CPU.
+ */
+
+ affinity_set(CPU_CURRENT);
+
+ cpu_ready_set = 0;
+
+ for (who = 0; who < ncpus; who++) {
+
+ wc_cpu_t *cpup = wc_other_cpus + who;
+ wc_desctbr_t gdt;
+
+ if (who == cpuid)
+ continue;
+
+ if (!CPU_IN_SET(mp_cpus, who))
+ continue;
+
+ PMD(PMD_SX, ("%s() waking up %d cpu\n", str, who))
+
+ bcopy(cpup, &(wp->wc_cpu), sizeof (wc_cpu_t));
+
+ gdt.base = cpup->wc_gdt_base;
+ gdt.limit = cpup->wc_gdt_limit;
+
+#if defined(__amd64)
+ code_length = (uint32_t)wc_long_mode_64 - (uint32_t)wc_rm_start;
+#else
+ code_length = 0;
+#endif
+
+ init_real_mode_platter(who, code_length, cpup->wc_cr4, gdt);
+
+ started_cpu = 1;
+
+ if ((err = mach_cpuid_start(who, rm_platter_va)) != 0) {
+ cmn_err(CE_WARN, "cpu%d: failed to start during "
+ "suspend/resume error %d", who, err);
+ continue;
+ }
+
+ PMD(PMD_SX, ("%s() #1 waiting for procset 0x%lx\n", str,
+ (ulong_t)procset))
+
+/*
+ * This conditional compile only affects the MP case.
+ */
+#ifdef MP_PM
+ for (delays = 0; !CPU_IN_SET(procset, who); delays++) {
+ if (delays == 500) {
+ /*
+ * After five seconds, things are probably
+ * looking a bit bleak - explain the hang.
+ */
+ cmn_err(CE_NOTE, "cpu%d: started, "
+ "but not running in the kernel yet", who);
+ PMD(PMD_SX, ("%s() %d cpu started "
+ "but not running in the kernel yet\n",
+ str, who))
+ } else if (delays > 2000) {
+ /*
+ * We waited at least 20 seconds, bail ..
+ */
+ cmn_err(CE_WARN, "cpu%d: timed out", who);
+ PMD(PMD_SX, ("%s() %d cpu timed out\n",
+ str, who))
+ started_cpu = 0;
+ }
+
+ /*
+ * wait at least 10ms, then check again..
+ */
+ delay(USEC_TO_TICK_ROUNDUP(10000));
+ }
+#else
+ while (!CPU_IN_SET(procset, who)) {
+ ;
+ }
+
+#endif /* MP_PM */
+
+ PMD(PMD_SX, ("%s() %d cpu started\n", str, who))
+
+ if (!started_cpu)
+ continue;
+
+ PMD(PMD_SX, ("%s() tsc_ready = %d\n", str,
+ get_tsc_ready()))
+
+ if (tsc_gethrtime_enable) {
+ PMD(PMD_SX, ("%s() calling tsc_sync_master\n", str))
+ tsc_sync_master(who);
+ }
+
+
+ PMD(PMD_SX, ("%s() waiting for cpu_ready_set %ld\n", str,
+ cpu_ready_set))
+ /*
+ * Wait for cpu to declare that it is ready, we want the
+ * cpus to start serially instead of in parallel, so that
+ * they do not contend with each other in wc_rm_start()
+ */
+ while (!CPU_IN_SET(cpu_ready_set, who)) {
+ PMD(PMD_SX, ("%s() waiting for "
+ "cpu_ready_set %ld\n", str, cpu_ready_set))
+ ;
+ }
+
+ /*
+ * do not need to re-initialize dtrace using dtrace_cpu_init
+ * function
+ */
+ PMD(PMD_SX, ("%s() cpu %d now ready\n", str, who))
+ }
+
+ affinity_clear();
+
+ PMD(PMD_SX, ("%s() all cpus now ready\n", str))
+}
+
+static void
+unmap_warm_reset_vector(ushort_t *warm_reset_vector)
+{
+ psm_unmap_phys((caddr_t)warm_reset_vector, sizeof (ushort_t *));
+}
+
+/*
+ * We need to setup a 1:1 (virtual to physical) mapping for the
+ * page containing the wakeup code.
+ */
+static struct as *save_as; /* when switching to kas */
+
+static void
+unmap_wakeaddr_1to1(uint64_t wakephys)
+{
+ uintptr_t wp = (uintptr_t)wakephys;
+ hat_setup(save_as->a_hat, 0); /* switch back from kernel hat */
+ hat_unload(kas.a_hat, (caddr_t)wp, PAGESIZE, HAT_UNLOAD);
+}
+
+void
+i_cpr_post_resume_cpus()
+{
+ uint64_t wakephys = rm_platter_pa;
+
+ if (warm_reset_vector != NULL)
+ unmap_warm_reset_vector(warm_reset_vector);
+
+ hat_unload(kas.a_hat, (caddr_t)(uintptr_t)rm_platter_pa, MMU_PAGESIZE,
+ HAT_UNLOAD);
+
+ /*
+ * cmi_post_mpstartup() is only required upon boot not upon
+ * resume from RAM
+ */
+
+ PT(PT_UNDO1to1);
+ /* Tear down 1:1 mapping for wakeup code */
+ unmap_wakeaddr_1to1(wakephys);
+}
+
+/* ARGSUSED */
+void
+i_cpr_handle_xc(int flag)
+{
+}
+
+int
+i_cpr_reusable_supported(void)
+{
+ return (0);
+}
+static void
+map_wakeaddr_1to1(uint64_t wakephys)
+{
+ uintptr_t wp = (uintptr_t)wakephys;
+ hat_devload(kas.a_hat, (caddr_t)wp, PAGESIZE, btop(wakephys),
+ (PROT_READ|PROT_WRITE|PROT_EXEC|HAT_STORECACHING_OK|HAT_NOSYNC),
+ HAT_LOAD);
+ save_as = curthread->t_procp->p_as;
+ hat_setup(kas.a_hat, 0); /* switch to kernel-only hat */
+}
+
+
+void
+prt_other_cpus()
+{
+ int who;
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("prt_other_cpus() other cpu table empty for "
+ "uniprocessor machine\n"))
+ return;
+ }
+
+ for (who = 0; who < ncpus; who++) {
+
+ wc_cpu_t *cpup = wc_other_cpus + who;
+
+ PMD(PMD_SX, ("prt_other_cpus() who = %d, gdt=%p:%x, "
+ "idt=%p:%x, ldt=%lx, tr=%lx, kgsbase="
+ AFMT ", sp=%lx\n", who,
+ (void *)cpup->wc_gdt_base, cpup->wc_gdt_limit,
+ (void *)cpup->wc_idt_base, cpup->wc_idt_limit,
+ (long)cpup->wc_ldt, (long)cpup->wc_tr,
+ (long)cpup->wc_kgsbase, (long)cpup->wc_rsp))
+ }
+}
+
+/*
+ * Power down the system.
+ */
+int
+i_cpr_power_down(int sleeptype)
+{
+ caddr_t wakevirt = rm_platter_va;
+ uint64_t wakephys = rm_platter_pa;
+ uint_t saved_intr;
+ uint32_t code_length = 0;
+ wc_desctbr_t gdt;
+ /*LINTED*/
+ wakecode_t *wp = (wakecode_t *)wakevirt;
+ /*LINTED*/
+ rm_platter_t *wcpp = (rm_platter_t *)wakevirt;
+ wc_cpu_t *cpup = &(wp->wc_cpu);
+ dev_info_t *ppm;
+ int ret = 0;
+ power_req_t power_req;
+ char *str = "i_cpr_power_down";
+#if defined(__amd64)
+ /*LINTED*/
+ rm_platter_t *real_mode_platter = (rm_platter_t *)rm_platter_va;
+#endif
+ extern int cpr_suspend_succeeded;
+ extern void kernel_wc_code();
+ extern ulong_t intr_clear(void);
+ extern void intr_restore(ulong_t);
+
+ ASSERT(sleeptype == CPR_TORAM);
+ ASSERT(CPU->cpu_id == 0);
+
+ if ((ppm = PPM(ddi_root_node())) == NULL) {
+ PMD(PMD_SX, ("%s: root node not claimed\n", str))
+ return (ENOTTY);
+ }
+
+ PMD(PMD_SX, ("Entering %s()\n", str))
+
+ PT(PT_IC);
+ saved_intr = intr_clear();
+
+ PT(PT_1to1);
+ /* Setup 1:1 mapping for wakeup code */
+ map_wakeaddr_1to1(wakephys);
+
+ PMD(PMD_SX, ("ncpus=%d\n", ncpus))
+
+ PMD(PMD_SX, ("wc_rm_end - wc_rm_start=%lx WC_CODESIZE=%x\n",
+ ((size_t)((uint_t)wc_rm_end - (uint_t)wc_rm_start)), WC_CODESIZE))
+
+ PMD(PMD_SX, ("wakevirt=%p, wakephys=%x\n",
+ (void *)wakevirt, (uint_t)wakephys))
+
+ ASSERT(((size_t)((uint_t)wc_rm_end - (uint_t)wc_rm_start)) <
+ WC_CODESIZE);
+
+ bzero(wakevirt, PAGESIZE);
+
+ /* Copy code to rm_platter */
+ bcopy((caddr_t)wc_rm_start, wakevirt,
+ (size_t)((uint_t)wc_rm_end - (uint_t)wc_rm_start));
+
+ prt_other_cpus();
+
+#if defined(__amd64)
+
+ PMD(PMD_SX, ("real_mode_platter->rm_cr4=%lx, getcr4()=%lx\n",
+ (ulong_t)real_mode_platter->rm_cr4, (ulong_t)getcr4()))
+ PMD(PMD_SX, ("real_mode_platter->rm_pdbr=%lx, getcr3()=%lx\n",
+ (ulong_t)real_mode_platter->rm_pdbr, getcr3()))
+
+ real_mode_platter->rm_cr4 = getcr4();
+ real_mode_platter->rm_pdbr = getcr3();
+
+ rmp_gdt_init(real_mode_platter);
+
+ /*
+ * Since the CPU needs to jump to protected mode using an identity
+ * mapped address, we need to calculate it here.
+ */
+ real_mode_platter->rm_longmode64_addr = rm_platter_pa +
+ ((uint32_t)wc_long_mode_64 - (uint32_t)wc_rm_start);
+
+ PMD(PMD_SX, ("real_mode_platter->rm_cr4=%lx, getcr4()=%lx\n",
+ (ulong_t)real_mode_platter->rm_cr4, getcr4()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_pdbr=%lx, getcr3()=%lx\n",
+ (ulong_t)real_mode_platter->rm_pdbr, getcr3()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_longmode64_addr=%lx\n",
+ (ulong_t)real_mode_platter->rm_longmode64_addr))
+
+#endif
+
+ PMD(PMD_SX, ("mp_cpus=%lx\n", (ulong_t)mp_cpus))
+
+ PT(PT_SC);
+ if (wc_save_context(cpup)) {
+
+ ret = i_cpr_platform_alloc(&(wc_other_cpus->wc_apic_state));
+ if (ret != 0)
+ return (ret);
+
+ ret = i_cpr_save_apic(&(wc_other_cpus->wc_apic_state));
+ PMD(PMD_SX, ("%s: i_cpr_save_apic() returned %d\n", str, ret))
+ if (ret != 0)
+ return (ret);
+
+ PMD(PMD_SX, ("wakephys=%x, kernel_wc_code=%p\n",
+ (uint_t)wakephys, (void *)&kernel_wc_code))
+ PMD(PMD_SX, ("virtaddr=%lx, retaddr=%lx\n",
+ (long)cpup->wc_virtaddr, (long)cpup->wc_retaddr))
+ PMD(PMD_SX, ("ebx=%x, edi=%x, esi=%x, ebp=%x, esp=%x\n",
+ cpup->wc_ebx, cpup->wc_edi, cpup->wc_esi, cpup->wc_ebp,
+ cpup->wc_esp))
+ PMD(PMD_SX, ("cr0=%lx, cr3=%lx, cr4=%lx\n",
+ (long)cpup->wc_cr0, (long)cpup->wc_cr3,
+ (long)cpup->wc_cr4))
+ PMD(PMD_SX, ("cs=%x, ds=%x, es=%x, ss=%x, fs=%lx, gs=%lx, "
+ "flgs=%lx\n", cpup->wc_cs, cpup->wc_ds, cpup->wc_es,
+ cpup->wc_ss, (long)cpup->wc_fs, (long)cpup->wc_gs,
+ (long)cpup->wc_eflags))
+
+ PMD(PMD_SX, ("gdt=%p:%x, idt=%p:%x, ldt=%lx, tr=%lx, "
+ "kgbase=%lx\n", (void *)cpup->wc_gdt_base,
+ cpup->wc_gdt_limit, (void *)cpup->wc_idt_base,
+ cpup->wc_idt_limit, (long)cpup->wc_ldt,
+ (long)cpup->wc_tr, (long)cpup->wc_kgsbase))
+
+ gdt.base = cpup->wc_gdt_base;
+ gdt.limit = cpup->wc_gdt_limit;
+
+#if defined(__amd64)
+ code_length = (uint32_t)wc_long_mode_64 -
+ (uint32_t)wc_rm_start;
+#else
+ code_length = 0;
+#endif
+
+ init_real_mode_platter(0, code_length, cpup->wc_cr4, gdt);
+
+#if defined(__amd64)
+ PMD(PMD_SX, ("real_mode_platter->rm_cr4=%lx, getcr4()=%lx\n",
+ (ulong_t)wcpp->rm_cr4, getcr4()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_pdbr=%lx, getcr3()=%lx\n",
+ (ulong_t)wcpp->rm_pdbr, getcr3()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_longmode64_addr=%lx\n",
+ (ulong_t)wcpp->rm_longmode64_addr))
+
+ PMD(PMD_SX,
+ ("real_mode_platter->rm_temp_gdt[TEMPGDT_KCODE64]=%lx\n",
+ (ulong_t)wcpp->rm_temp_gdt[TEMPGDT_KCODE64]))
+#endif
+
+ PMD(PMD_SX, ("gdt=%p:%x, idt=%p:%x, ldt=%lx, tr=%lx, "
+ "kgsbase=%lx\n", (void *)wcpp->rm_gdt_base,
+ wcpp->rm_gdt_lim, (void *)wcpp->rm_idt_base,
+ wcpp->rm_idt_lim, (long)cpup->wc_ldt, (long)cpup->wc_tr,
+ (long)cpup->wc_kgsbase))
+
+ power_req.request_type = PMR_PPM_ENTER_SX;
+ power_req.req.ppm_power_enter_sx_req.sx_state = S3;
+ power_req.req.ppm_power_enter_sx_req.test_point =
+ cpr_test_point;
+ power_req.req.ppm_power_enter_sx_req.wakephys = wakephys;
+
+ PMD(PMD_SX, ("%s: pm_ctlops PMR_PPM_ENTER_SX\n", str))
+ PT(PT_PPMCTLOP);
+ (void) pm_ctlops(ppm, ddi_root_node(), DDI_CTLOPS_POWER,
+ &power_req, &ret);
+ PMD(PMD_SX, ("%s: returns %d\n", str, ret))
+
+ /*
+ * If it works, we get control back to the else branch below
+ * If we get control back here, it didn't work.
+ * XXX return EINVAL here?
+ */
+
+ unmap_wakeaddr_1to1(wakephys);
+ intr_restore(saved_intr);
+
+ return (ret);
+ } else {
+ cpr_suspend_succeeded = 1;
+
+ power_req.request_type = PMR_PPM_EXIT_SX;
+ power_req.req.ppm_power_enter_sx_req.sx_state = S3;
+
+ PMD(PMD_SX, ("%s: pm_ctlops PMR_PPM_EXIT_SX\n", str))
+ PT(PT_PPMCTLOP);
+ (void) pm_ctlops(ppm, ddi_root_node(), DDI_CTLOPS_POWER,
+ &power_req, &ret);
+ PMD(PMD_SX, ("%s: returns %d\n", str, ret))
+
+ ret = i_cpr_restore_apic(&(wc_other_cpus->wc_apic_state));
+ /*
+ * the restore should never fail, if the saved suceeded
+ */
+ ASSERT(ret == 0);
+
+ i_cpr_platform_free(&(wc_other_cpus->wc_apic_state));
+
+ PT(PT_INTRRESTORE);
+ intr_restore(saved_intr);
+ PT(PT_CPU);
+
+ return (ret);
+ }
+}
+
+/*
+ * Stop all other cpu's before halting or rebooting. We pause the cpu's
+ * instead of sending a cross call.
+ * Stolen from sun4/os/mp_states.c
+ */
+
+static int cpu_are_paused; /* sic */
+
+void
+i_cpr_stop_other_cpus(void)
+{
+ mutex_enter(&cpu_lock);
+ if (cpu_are_paused) {
+ mutex_exit(&cpu_lock);
+ return;
+ }
+ pause_cpus(NULL);
+ cpu_are_paused = 1;
+
+ mutex_exit(&cpu_lock);
+}
+
+int
+i_cpr_is_supported(int sleeptype)
+{
+ extern int cpr_supported_override;
+ extern int cpr_platform_enable;
+ extern int pm_S3_enabled;
+
+ if (sleeptype != CPR_TORAM)
+ return (0);
+
+ /*
+ * The next statement tests if a specific platform has turned off
+ * cpr support.
+ */
+ if (cpr_supported_override)
+ return (0);
+
+ /*
+ * If a platform has specifically turned on cpr support ...
+ */
+ if (cpr_platform_enable)
+ return (1);
+
+ return (pm_S3_enabled);
+}
+
+void
+i_cpr_bitmap_cleanup(void)
+{
+}
+
+void
+i_cpr_free_memory_resources(void)
+{
+}
+
+/*
+ * Needed only for S3 so far
+ */
+static int
+i_cpr_platform_alloc(psm_state_request_t *req)
+{
+ char *str = "i_cpr_platform_alloc";
+
+ PMD(PMD_SX, ("cpu = %d, %s(%p) \n", CPU->cpu_id, str, (void *)req))
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ return (0);
+ }
+
+ req->psr_cmd = PSM_STATE_ALLOC;
+ return ((*psm_state)(req));
+}
+
+/*
+ * Needed only for S3 so far
+ */
+static void
+i_cpr_platform_free(psm_state_request_t *req)
+{
+ char *str = "i_cpr_platform_free";
+
+ PMD(PMD_SX, ("cpu = %d, %s(%p) \n", CPU->cpu_id, str, (void *)req))
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ }
+
+ req->psr_cmd = PSM_STATE_FREE;
+ (void) (*psm_state)(req);
+}
+
+static int
+i_cpr_save_apic(psm_state_request_t *req)
+{
+ char *str = "i_cpr_save_apic";
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ return (0);
+ }
+
+ req->psr_cmd = PSM_STATE_SAVE;
+ return ((*psm_state)(req));
+}
+
+static int
+i_cpr_restore_apic(psm_state_request_t *req)
+{
+ char *str = "i_cpr_restore_apic";
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ return (0);
+ }
+
+ req->psr_cmd = PSM_STATE_RESTORE;
+ return ((*psm_state)(req));
+}
+
+
+/* stop lint complaining about offset not being used in 32bit mode */
+#if !defined(__amd64)
+/*ARGSUSED*/
+#endif
+static void
+init_real_mode_platter(int cpun, uint32_t offset, uint_t cr4, wc_desctbr_t gdt)
+{
+ /*LINTED*/
+ rm_platter_t *real_mode_platter = (rm_platter_t *)rm_platter_va;
+
+ /*
+ * Fill up the real mode platter to make it easy for real mode code to
+ * kick it off. This area should really be one passed by boot to kernel
+ * and guaranteed to be below 1MB and aligned to 16 bytes. Should also
+ * have identical physical and virtual address in paged mode.
+ */
+
+ real_mode_platter->rm_pdbr = getcr3();
+ real_mode_platter->rm_cpu = cpun;
+ real_mode_platter->rm_cr4 = cr4;
+
+ real_mode_platter->rm_gdt_base = gdt.base;
+ real_mode_platter->rm_gdt_lim = gdt.limit;
+
+#if defined(__amd64)
+ real_mode_platter->rm_x86feature = x86_feature;
+
+ if (getcr3() > 0xffffffffUL)
+ panic("Cannot initialize CPUs; kernel's 64-bit page tables\n"
+ "located above 4G in physical memory (@ 0x%llx).",
+ (unsigned long long)getcr3());
+
+ /*
+ * Setup pseudo-descriptors for temporary GDT and IDT for use ONLY
+ * by code in real_mode_start():
+ *
+ * GDT[0]: NULL selector
+ * GDT[1]: 64-bit CS: Long = 1, Present = 1, bits 12, 11 = 1
+ *
+ * Clear the IDT as interrupts will be off and a limit of 0 will cause
+ * the CPU to triple fault and reset on an NMI, seemingly as reasonable
+ * a course of action as any other, though it may cause the entire
+ * platform to reset in some cases...
+ */
+ real_mode_platter->rm_temp_gdt[0] = 0ULL;
+ real_mode_platter->rm_temp_gdt[TEMPGDT_KCODE64] = 0x20980000000000ULL;
+
+ real_mode_platter->rm_temp_gdt_lim = (ushort_t)
+ (sizeof (real_mode_platter->rm_temp_gdt) - 1);
+ real_mode_platter->rm_temp_gdt_base = rm_platter_pa +
+ (uint32_t)(&((rm_platter_t *)0)->rm_temp_gdt);
+
+ real_mode_platter->rm_temp_idt_lim = 0;
+ real_mode_platter->rm_temp_idt_base = 0;
+
+ /*
+ * Since the CPU needs to jump to protected mode using an identity
+ * mapped address, we need to calculate it here.
+ */
+ real_mode_platter->rm_longmode64_addr = rm_platter_pa + offset;
+#endif /* __amd64 */
+
+ /* return; */
+}
+
+void
+i_cpr_start_cpu(void)
+{
+
+ struct cpu *cp = CPU;
+
+ char *str = "i_cpr_start_cpu";
+ extern void init_cpu_syscall(struct cpu *cp);
+
+#if defined(__amd64)
+ wc_cpu_t *cpup = wc_other_cpus + cp->cpu_id;
+#endif /* __amd64 */
+
+ PMD(PMD_SX, ("%s() called\n", str))
+
+ PMD(PMD_SX, ("%s() #0 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ mutex_enter(&cpu_lock);
+ if (cp == i_cpr_bootcpu()) {
+ mutex_exit(&cpu_lock);
+ PMD(PMD_SX,
+ ("%s() called on bootcpu nothing to do!\n", str))
+ return;
+ }
+ mutex_exit(&cpu_lock);
+
+ /*
+ * We need to Sync PAT with cpu0's PAT. We have to do
+ * this with interrupts disabled.
+ */
+ if (x86_feature & X86_PAT)
+ pat_sync();
+
+ /*
+ * Initialize this CPU's syscall handlers
+ */
+ init_cpu_syscall(cp);
+
+ PMD(PMD_SX, ("%s() #1 cp->cpu_base_spl %d\n", str, cp->cpu_base_spl))
+
+ /*
+ * Do not need to call cpuid_pass2(), cpuid_pass3(), cpuid_pass4() or
+ * init_cpu_info(), since the work that they do is only needed to
+ * be done once at boot time
+ */
+
+
+ mutex_enter(&cpu_lock);
+
+#if defined(__amd64)
+ restore_stack(cpup);
+#endif /* __amd64 */
+
+ CPUSET_ADD(procset, cp->cpu_id);
+ mutex_exit(&cpu_lock);
+
+ PMD(PMD_SX, ("%s() #2 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ /* XXX remove before integration */
+ PMD(PMD_SX, ("%s() procset 0x%lx\n", str, (ulong_t)procset))
+
+ if (tsc_gethrtime_enable) {
+ PMD(PMD_SX, ("%s() calling tsc_sync_slave\n", str))
+ tsc_sync_slave();
+ }
+
+ PMD(PMD_SX, ("%s() cp->cpu_id %d, cp->cpu_intr_actv %d\n", str,
+ cp->cpu_id, cp->cpu_intr_actv))
+ PMD(PMD_SX, ("%s() #3 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ (void) spl0(); /* enable interrupts */
+
+ PMD(PMD_SX, ("%s() #4 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ /*
+ * Set up the CPU module for this CPU. This can't be done before
+ * this CPU is made CPU_READY, because we may (in heterogeneous systems)
+ * need to go load another CPU module. The act of attempting to load
+ * a module may trigger a cross-call, which will ASSERT unless this
+ * cpu is CPU_READY.
+ */
+
+ /*
+ * cmi already been init'd (during boot), so do not need to do it again
+ */
+#ifdef PM_REINITMCAONRESUME
+ if (x86_feature & X86_MCA)
+ cmi_mca_init();
+#endif
+
+ PMD(PMD_SX, ("%s() returning\n", str))
+
+ /* return; */
+}
+
+#if defined(__amd64)
+/*
+ * we only need to do this for amd64!
+ */
+
+/*
+ * save the stack
+ */
+void
+save_stack(wc_cpu_t *cpup)
+{
+ char *str = "save_stack";
+ caddr_t base = curthread->t_stk;
+ caddr_t sp = (caddr_t)cpup->wc_rsp;
+
+
+ PMD(PMD_SX, ("%s() CPU->cpu_id %d\n", str, CPU->cpu_id))
+ PMD(PMD_SX, ("save_stack() curthread->t_stk = %p, sp = %p\n",
+ (void *)base, (void *)sp))
+
+ ASSERT(base > sp);
+ /*LINTED*/
+ bcopy(sp, cpup->wc_stack, base - sp);
+
+}
+
+/*
+ * restore the stack
+ */
+static void
+restore_stack(wc_cpu_t *cpup)
+{
+ /*
+ * we only need to do this for amd64!
+ */
+
+ char *str = "restore_stack";
+ caddr_t base = curthread->t_stk;
+ caddr_t sp = (caddr_t)cpup->wc_rsp;
+
+ PMD(PMD_SX, ("%s() CPU->cpu_id %d\n", str, CPU->cpu_id))
+ PMD(PMD_SX, ("%s() curthread->t_stk = %p, sp = %p\n", str,
+ (void *)base, (void *)sp))
+
+ ASSERT(base > sp);
+ /*LINTED*/
+ bcopy(cpup->wc_stack, sp, base - sp);
+
+}
+
+#endif /* __amd64 */
+
+
+void
+i_cpr_alloc_cpus(void)
+{
+ char *str = "i_cpr_alloc_cpus";
+
+ PMD(PMD_SX, ("%s() CPU->cpu_id %d\n", str, CPU->cpu_id))
+ /*
+ * we allocate this only when we actually need it to save on
+ * kernel memory
+ */
+
+ if (wc_other_cpus == NULL) {
+ wc_other_cpus = kmem_zalloc(ncpus * sizeof (wc_cpu_t),
+ KM_SLEEP);
+ }
+
+}
+
+void
+i_cpr_free_cpus(void)
+{
+ if (wc_other_cpus != NULL) {
+ kmem_free((void *) wc_other_cpus, ncpus * sizeof (wc_cpu_t));
+ wc_other_cpus = NULL;
+ }
+}
+
+/*
+ * wrapper for acpica_ddi_save_resources()
+ */
+void
+i_cpr_save_configuration(dev_info_t *dip)
+{
+ acpica_ddi_save_resources(dip);
+}
+
+/*
+ * wrapper for acpica_ddi_restore_resources()
+ */
+void
+i_cpr_restore_configuration(dev_info_t *dip)
+{
+ acpica_ddi_restore_resources(dip);
+}
diff --git a/usr/src/uts/i86pc/os/ddi_impl.c b/usr/src/uts/i86pc/os/ddi_impl.c
index ce92a564ec..1ddc6aa0f7 100644
--- a/usr/src/uts/i86pc/os/ddi_impl.c
+++ b/usr/src/uts/i86pc/os/ddi_impl.c
@@ -73,9 +73,10 @@
*/
/*
- * No platform drivers on this platform
+ * Platform drivers on this platform
*/
char *platform_module_list[] = {
+ "acpippm",
"ppm",
(char *)0
};
@@ -1947,9 +1948,18 @@ get_vga_properties(void)
char property_val[50];
void *bop_staging_area;
- major = ddi_name_to_major("vgatext");
- if (major == (major_t)-1)
- return;
+ /*
+ * XXXX Hack Allert!
+ * There really needs to be a better way for identifying various
+ * console framebuffers and their related issues. Till then,
+ * check for this one as a replacement to vgatext.
+ */
+ major = ddi_name_to_major("ragexl");
+ if (major == (major_t)-1) {
+ major = ddi_name_to_major("vgatext");
+ if (major == (major_t)-1)
+ return;
+ }
devi = devnamesp[major].dn_head;
if (devi == NULL)
return;
diff --git a/usr/src/uts/i86pc/os/memscrub.c b/usr/src/uts/i86pc/os/memscrub.c
index 322573ff91..b38cd4ecfb 100644
--- a/usr/src/uts/i86pc/os/memscrub.c
+++ b/usr/src/uts/i86pc/os/memscrub.c
@@ -98,6 +98,7 @@
#include <vm/seg_kmem.h>
#include <vm/seg_kpm.h>
#include <vm/hat_i86.h>
+#include <sys/callb.h> /* CPR callback */
static caddr_t memscrub_window;
static hat_mempte_t memscrub_pte;
@@ -252,7 +253,7 @@ compute_interval_sec()
return (memscrub_period_sec);
else
return (memscrub_period_sec/
- (memscrub_phys_pages/memscrub_span_pages));
+ (memscrub_phys_pages/memscrub_span_pages));
}
void
@@ -266,6 +267,12 @@ memscrubber()
struct memlist *mlp;
extern void scan_memory(caddr_t, size_t);
+ callb_cpr_t cprinfo;
+
+ /*
+ * notify CPR of our existence
+ */
+ CALLB_CPR_INIT(&cprinfo, &memscrub_lock, callb_generic_cpr, "memscrub");
if (memscrub_memlist == NULL) {
cmn_err(CE_WARN, "memscrub_memlist not initialized.");
@@ -314,6 +321,12 @@ memscrubber()
}
/*
+ * it is safe from our standpoint for CPR to
+ * suspend the system
+ */
+ CALLB_CPR_SAFE_BEGIN(&cprinfo);
+
+ /*
* hit the snooze bar
*/
(void) timeout(memscrub_wakeup, NULL, interval_sec * hz);
@@ -323,6 +336,9 @@ memscrubber()
*/
cv_wait(&memscrub_cv, &memscrub_lock);
+ /* we need to goto work */
+ CALLB_CPR_SAFE_END(&cprinfo, &memscrub_lock);
+
mutex_exit(&memscrub_lock);
do {
@@ -393,6 +409,12 @@ memscrub_exit:
if (!disable_memscrub_quietly)
cmn_err(CE_NOTE, "memory scrubber exiting.");
+ /*
+ * We are about to bail, but don't have the memscrub_lock,
+ * and it is needed for CALLB_CPR_EXIT.
+ */
+ mutex_enter(&memscrub_lock);
+ CALLB_CPR_EXIT(&cprinfo);
cv_destroy(&memscrub_cv);
@@ -448,7 +470,7 @@ memscrub_add_span(uint64_t start, uint64_t bytes)
memscrub_printmemlist("memscrub_memlist before", memscrub_memlist);
cmn_err(CE_CONT, "memscrub_phys_pages: 0x%x\n", memscrub_phys_pages);
cmn_err(CE_CONT, "memscrub_add_span: address: 0x%llx"
- " size: 0x%llx\n", start, bytes);
+ " size: 0x%llx\n", start, bytes);
#endif /* MEMSCRUB_DEBUG */
/*
diff --git a/usr/src/uts/i86pc/os/mp_implfuncs.c b/usr/src/uts/i86pc/os/mp_implfuncs.c
index ac3e03ff45..5ffbd7502a 100644
--- a/usr/src/uts/i86pc/os/mp_implfuncs.c
+++ b/usr/src/uts/i86pc/os/mp_implfuncs.c
@@ -30,7 +30,7 @@
#include <vm/hat.h>
#include <sys/modctl.h>
#include <vm/seg_kmem.h>
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/psm.h>
#include <sys/psm_modctl.h>
#include <sys/smp_impldefs.h>
diff --git a/usr/src/uts/i86pc/os/mp_machdep.c b/usr/src/uts/i86pc/os/mp_machdep.c
index 1d4fef5395..93332f1382 100644
--- a/usr/src/uts/i86pc/os/mp_machdep.c
+++ b/usr/src/uts/i86pc/os/mp_machdep.c
@@ -25,7 +25,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/smp_impldefs.h>
#include <sys/psm.h>
#include <sys/psm_modctl.h>
@@ -136,6 +136,8 @@ void (*psm_timer_disable)(void) = NULL;
void (*psm_post_cyclic_setup)(void *arg) = NULL;
int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, psm_intr_op_t,
int *) = mach_intr_ops;
+int (*psm_state)(psm_state_request_t *) = (int (*)(psm_state_request_t *))
+ return_instr;
void (*notify_error)(int, char *) = (void (*)(int, char *))return_instr;
void (*hrtime_tick)(void) = return_instr;
@@ -923,6 +925,9 @@ mach_smpinit(void)
if (pops->psm_post_cyclic_setup)
psm_post_cyclic_setup = pops->psm_post_cyclic_setup;
+ if (pops->psm_state)
+ psm_state = pops->psm_state;
+
/* check for multiple cpu's */
if (cnt < 2)
return;
@@ -1314,6 +1319,18 @@ mach_cpu_start(struct cpu *cp, void *ctx)
return ((*pops->psm_cpu_start)(id, ctx));
}
+int
+mach_cpuid_start(processorid_t id, void *ctx)
+{
+ struct psm_ops *pops = mach_set[0];
+
+#ifdef DEBUG
+ if (CPU_IN_SET(cpufailset, id))
+ return (0);
+#endif
+ return ((*pops->psm_cpu_start)(id, ctx));
+}
+
/*ARGSUSED*/
static int
mach_translate_irq(dev_info_t *dip, int irqno)
diff --git a/usr/src/uts/i86pc/os/mp_pc.c b/usr/src/uts/i86pc/os/mp_pc.c
index eb07c7bd4f..7746818a8f 100644
--- a/usr/src/uts/i86pc/os/mp_pc.c
+++ b/usr/src/uts/i86pc/os/mp_pc.c
@@ -47,6 +47,9 @@
extern void real_mode_start(void);
extern void real_mode_end(void);
+extern void *(*cpu_pause_func)(void *);
+
+void rmp_gdt_init(rm_platter_t *);
/*
* Fill up the real mode platter to make it easy for real mode code to
@@ -68,8 +71,8 @@ mach_cpucontext_init(void)
* setup secondary cpu bios boot up vector
*/
*vec = (ushort_t)((caddr_t)
- ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
- + ((ulong_t)rm_platter_va & 0xf));
+ ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
+ + ((ulong_t)rm_platter_va & 0xf));
vec[1] = (ushort_t)(rm_platter_pa >> 4);
warm_reset_vector = vec;
@@ -162,6 +165,16 @@ mach_cpucontext_alloc(struct cpu *cp)
rm->rm_x86feature = x86_feature;
rm->rm_cr4 = getcr4();
+ rmp_gdt_init(rm);
+
+ return (ct);
+}
+
+/*ARGSUSED*/
+void
+rmp_gdt_init(rm_platter_t *rm)
+{
+
#if defined(__amd64)
if (getcr3() > 0xffffffffUL)
@@ -197,7 +210,6 @@ mach_cpucontext_alloc(struct cpu *cp)
((uint32_t)long_mode_64 - (uint32_t)real_mode_start);
#endif /* __amd64 */
- return (ct);
}
/*ARGSUSED*/
diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c
index 87f945811a..4feca10ed7 100644
--- a/usr/src/uts/i86pc/os/mp_startup.c
+++ b/usr/src/uts/i86pc/os/mp_startup.c
@@ -148,7 +148,7 @@ init_cpu_info(struct cpu *cp)
* Configure syscall support on this CPU.
*/
/*ARGSUSED*/
-static void
+void
init_cpu_syscall(struct cpu *cp)
{
kpreempt_disable();
diff --git a/usr/src/uts/i86pc/os/timestamp.c b/usr/src/uts/i86pc/os/timestamp.c
index fe49b90f2a..23d9957d13 100644
--- a/usr/src/uts/i86pc/os/timestamp.c
+++ b/usr/src/uts/i86pc/os/timestamp.c
@@ -160,6 +160,7 @@ static hrtime_t shadow_tsc_hrtime_base;
static hrtime_t shadow_tsc_last;
static uint_t shadow_nsec_scale;
static uint32_t shadow_hres_lock;
+int get_tsc_ready();
hrtime_t
tsc_gethrtime(void)
@@ -409,7 +410,8 @@ tsc_digest(processorid_t target)
if ((tdelta > max) || ((tdelta >= 0) && update)) {
TSC_CONVERT_AND_ADD(tdelta, hdelta, nsec_scale);
tsc_sync_delta[target] = tsc_sync_delta[source] - hdelta;
- tsc_sync_tick_delta[target] = -tdelta;
+ tsc_sync_tick_delta[target] = tsc_sync_tick_delta[source]
+ -tdelta;
gethrtimef = tsc_gethrtime_delta;
gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
return;
@@ -419,7 +421,8 @@ tsc_digest(processorid_t target)
if ((tdelta > max) || update) {
TSC_CONVERT_AND_ADD(tdelta, hdelta, nsec_scale);
tsc_sync_delta[target] = tsc_sync_delta[source] + hdelta;
- tsc_sync_tick_delta[target] = tdelta;
+ tsc_sync_tick_delta[target] = tsc_sync_tick_delta[source]
+ + tdelta;
gethrtimef = tsc_gethrtime_delta;
gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
}
@@ -621,3 +624,166 @@ tsc_hrtimeinit(uint64_t cpu_freq_hz)
hrtime_tick = tsc_tick;
gethrtime_hires = 1;
}
+
+int
+get_tsc_ready()
+{
+ return (tsc_ready);
+}
+
+/*
+ * Adjust all the deltas by adding the passed value to the array.
+ * Then use the "delt" versions of the the gethrtime functions.
+ * Note that 'tdelta' _could_ be a negative number, which should
+ * reduce the values in the array (used, for example, if the Solaris
+ * instance was moved by a virtual manager to a machine with a higher
+ * value of tsc).
+ */
+void
+tsc_adjust_delta(hrtime_t tdelta)
+{
+ int i;
+ hrtime_t hdelta = 0;
+
+ TSC_CONVERT(tdelta, hdelta, nsec_scale);
+
+ for (i = 0; i < NCPU; i++) {
+ tsc_sync_delta[i] += hdelta;
+ tsc_sync_tick_delta[i] += tdelta;
+ }
+
+ gethrtimef = tsc_gethrtime_delta;
+ gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
+}
+
+/*
+ * Functions to manage TSC and high-res time on suspend and resume.
+ */
+
+/*
+ * declarations needed for time adjustment
+ */
+extern void rtcsync(void);
+extern tod_ops_t *tod_ops;
+/* There must be a better way than exposing nsec_scale! */
+extern uint_t nsec_scale;
+static uint64_t tsc_saved_tsc = 0; /* 1 in 2^64 chance this'll screw up! */
+static timestruc_t tsc_saved_ts;
+static int tsc_needs_resume = 0; /* We only want to do this once. */
+int tsc_delta_onsuspend = 0;
+int tsc_adjust_seconds = 1;
+int tsc_suspend_count = 0;
+int tsc_resume_in_cyclic = 0;
+
+/*
+ * Let timestamp.c know that we are suspending. It needs to take
+ * snapshots of the current time, and do any pre-suspend work.
+ */
+void
+tsc_suspend(void)
+{
+/*
+ * What we need to do here, is to get the time we suspended, so that we
+ * know how much we should add to the resume.
+ * This routine is called by each CPU, so we need to handle reentry.
+ */
+ if (tsc_gethrtime_enable) {
+ /*
+ * We put the tsc_read() inside the lock as it
+ * as no locking constraints, and it puts the
+ * aquired value closer to the time stamp (in
+ * case we delay getting the lock).
+ */
+ mutex_enter(&tod_lock);
+ tsc_saved_tsc = tsc_read();
+ tsc_saved_ts = TODOP_GET(tod_ops);
+ mutex_exit(&tod_lock);
+ /* We only want to do this once. */
+ if (tsc_needs_resume == 0) {
+ if (tsc_delta_onsuspend) {
+ tsc_adjust_delta(tsc_saved_tsc);
+ } else {
+ tsc_adjust_delta(nsec_scale);
+ }
+ tsc_suspend_count++;
+ }
+ }
+
+ invalidate_cache();
+ tsc_needs_resume = 1;
+}
+
+/*
+ * Restore all timestamp state based on the snapshots taken at
+ * suspend time.
+ */
+void
+tsc_resume(void)
+{
+ /*
+ * We only need to (and want to) do this once. So let the first
+ * caller handle this (we are locked by the cpu lock), as it
+ * is preferential that we get the earliest sync.
+ */
+ if (tsc_needs_resume) {
+ /*
+ * If using the TSC, adjust the delta based on how long
+ * we were sleeping (or away). We also adjust for
+ * migration and a grown TSC.
+ */
+ if (tsc_saved_tsc != 0) {
+ timestruc_t ts;
+ hrtime_t now, sleep_tsc = 0;
+ int sleep_sec;
+ extern void tsc_tick(void);
+ extern uint64_t cpu_freq_hz;
+
+ /* tsc_read() MUST be before TODOP_GET() */
+ mutex_enter(&tod_lock);
+ now = tsc_read();
+ ts = TODOP_GET(tod_ops);
+ mutex_exit(&tod_lock);
+
+ /* Compute seconds of sleep time */
+ sleep_sec = ts.tv_sec - tsc_saved_ts.tv_sec;
+
+ /*
+ * If the saved sec is less that or equal to
+ * the current ts, then there is likely a
+ * problem with the clock. Assume at least
+ * one second has passed, so that time goes forward.
+ */
+ if (sleep_sec <= 0) {
+ sleep_sec = 1;
+ }
+
+ /* How many TSC's should have occured while sleeping */
+ if (tsc_adjust_seconds)
+ sleep_tsc = sleep_sec * cpu_freq_hz;
+
+ /*
+ * We also want to subtract from the "sleep_tsc"
+ * the current value of tsc_read(), so that our
+ * adjustment accounts for the amount of time we
+ * have been resumed _or_ an adjustment based on
+ * the fact that we didn't actually power off the
+ * CPU (migration is another issue, but _should_
+ * also comply with this calculation). If the CPU
+ * never powered off, then:
+ * 'now == sleep_tsc + saved_tsc'
+ * and the delta will effectively be "0".
+ */
+ sleep_tsc -= now;
+ if (tsc_delta_onsuspend) {
+ tsc_adjust_delta(sleep_tsc);
+ } else {
+ tsc_adjust_delta(tsc_saved_tsc + sleep_tsc);
+ }
+ tsc_saved_tsc = 0;
+
+ tsc_tick();
+ }
+ tsc_needs_resume = 0;
+ }
+
+}
diff --git a/usr/src/uts/i86pc/sys/apic.h b/usr/src/uts/i86pc/sys/apic.h
index 0e692d954b..f4f57e1059 100644
--- a/usr/src/uts/i86pc/sys/apic.h
+++ b/usr/src/uts/i86pc/sys/apic.h
@@ -699,6 +699,7 @@ extern int apic_rebind_all(apic_irq_t *irq_ptr, int bind_cpu);
extern int apic_introp_xlate(dev_info_t *dip, struct intrspec *ispec, int type);
extern int apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp,
psm_intr_op_t intr_op, int *result);
+extern int apic_state(psm_state_request_t *);
extern boolean_t apic_cpu_in_range(int cpu);
extern int apic_check_msi_support();
extern apic_irq_t *apic_find_irq(dev_info_t *dip, struct intrspec *ispec,
diff --git a/usr/src/uts/i86pc/sys/cpr_impl.h b/usr/src/uts/i86pc/sys/cpr_impl.h
new file mode 100644
index 0000000000..72b76f42d4
--- /dev/null
+++ b/usr/src/uts/i86pc/sys/cpr_impl.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_CPR_IMPL_H
+#define _SYS_CPR_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef _ASM
+
+#include <sys/processor.h>
+#include <sys/machparam.h>
+#include <sys/vnode.h>
+#include <sys/pte.h>
+
+/*
+ * This file contains machine dependent information for CPR
+ */
+#define CPR_MACHTYPE_X86 0x5856 /* 'X'0t86 */
+typedef uint64_t cpr_ptr;
+typedef uint64_t cpr_ext;
+
+
+/*
+ * processor info
+ */
+struct i86pc_cpu_info {
+ pnode_t node;
+ processorid_t cpu_id;
+};
+
+extern void i_cpr_machdep_setup(void);
+extern void i_cpr_enable_intr(void);
+extern void i_cpr_stop_intr(void);
+extern void i_cpr_handle_xc(int);
+extern int i_cpr_check_cprinfo(void);
+extern int i_cpr_reusable_supported(void);
+
+#endif /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_CPR_IMPL_H */
diff --git a/usr/src/uts/i86pc/sys/cpr_wakecode.h b/usr/src/uts/i86pc/sys/cpr_wakecode.h
new file mode 100644
index 0000000000..7f55c9dcd6
--- /dev/null
+++ b/usr/src/uts/i86pc/sys/cpr_wakecode.h
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CPR_WC_H
+#define _CPR_WC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WC_CODESIZE 0x400
+
+#if ! defined(_ASM)
+
+#include <sys/rm_platter.h>
+#include <sys/psm_types.h>
+
+typedef struct wc_cpu {
+ uint64_t wc_retaddr;
+ uint64_t wc_virtaddr;
+ uint64_t wc_cr0;
+ uint64_t wc_cr3;
+ uint64_t wc_cr4;
+ uint64_t wc_cr8;
+ uint64_t wc_fs;
+ uint64_t wc_fsbase;
+ uint64_t wc_gs;
+ uint64_t wc_gsbase;
+ uint64_t wc_kgsbase;
+ uint64_t wc_r8;
+ uint64_t wc_r9;
+ uint64_t wc_r10;
+ uint64_t wc_r11;
+ uint64_t wc_r12;
+ uint64_t wc_r13;
+ uint64_t wc_r14;
+ uint64_t wc_r15;
+ uint64_t wc_rax;
+ uint64_t wc_rbp;
+ uint64_t wc_rbx;
+ uint64_t wc_rcx;
+ uint64_t wc_rdi;
+ uint64_t wc_rdx;
+ uint64_t wc_rsi;
+ uint64_t wc_rsp;
+
+#if defined(__amd64)
+ /*
+ * The compiler will want to 64-bit align the 64-bit rm_gdt_base
+ * pointer, so we need to add an extra four bytes of padding here to
+ * make sure rm_gdt_lim and rm_gdt_base will align to create a proper
+ * ten byte GDT pseudo-descriptor.
+ */
+uint32_t wc_gdt_pad1;
+#endif
+ ushort_t wc_gdt_pad2;
+ ushort_t wc_gdt_limit;
+ user_desc_t *wc_gdt_base;
+
+#if defined(__amd64)
+ /*
+ * The compiler will want to 64-bit align the 64-bit rm_idt_base
+ * pointer, so we need to add an extra four bytes of padding here to
+ * make sure rm_idt_lim and rm_idt_base will align to create a proper
+ * ten byte IDT pseudo-descriptor.
+ */
+uint32_t wc_idt_pad1;
+#endif
+ ushort_t wc_idt_pad2;
+ ushort_t wc_idt_limit;
+ user_desc_t *wc_idt_base;
+
+#if defined(__amd64)
+ uint64_t wc_tr;
+ uint64_t wc_ldt;
+ uint64_t wc_eflags;
+#else
+ uint32_t wc_tr;
+ uint32_t wc_ldt;
+ uint32_t wc_eflags;
+#endif
+
+ uint32_t wc_ebx;
+ uint32_t wc_edi;
+ uint32_t wc_esi;
+ uint32_t wc_ebp;
+ uint32_t wc_esp;
+ uint16_t wc_ss;
+ uint16_t wc_cs;
+ uint16_t wc_ds;
+ uint16_t wc_es;
+ char wc_stack[400];
+ psm_state_request_t wc_apic_state;
+
+
+ /* temp stack grows down to here */
+} wc_cpu_t;
+
+typedef struct wc_wakecode {
+ rm_platter_t wc_platter;
+ wc_cpu_t wc_cpu;
+
+ /* temp stack grows down to here */
+} wakecode_t;
+
+/*
+ * this is NOT correctly aligned, see description of idt & gdt, limit and
+ * base in wc_cpu_t above
+ */
+typedef struct wc_desctbr {
+ ushort_t limit;
+ void *base;
+} wc_desctbr_t;
+
+extern int wc_save_context(wc_cpu_t *);
+extern void wc_rm_start(void);
+extern void wc_rm_end(void);
+extern void (*cpr_start_cpu_func)(void);
+
+#endif /* ! defined(_ASM) */
+
+#define WC_STKSTART 0x7fc /* end of rm_platter page */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CPR_WC_H */
diff --git a/usr/src/uts/i86pc/sys/machclock.h b/usr/src/uts/i86pc/sys/machclock.h
index 6b3686b95c..a214ada1bc 100644
--- a/usr/src/uts/i86pc/sys/machclock.h
+++ b/usr/src/uts/i86pc/sys/machclock.h
@@ -40,14 +40,20 @@ extern "C" {
struct tod_ops;
typedef struct tod_ops tod_ops_t;
+/*
+ * TOD Ops.
+ * The only functions that _must_ be defined are the tod_get() and
+ * tod_set() functions. All others may be unused, and need to be
+ * checked for NULL before using.
+ */
struct tod_ops {
int tod_version;
timestruc_t (*tod_get)(tod_ops_t *);
void (*tod_set)(tod_ops_t *, timestruc_t);
- /*
- * On SPARC, additional operations include setting
- * and clearing a watchdog timer, as well as power alarms.
- */
+ uint_t (*tod_set_watchdog_timer)(tod_ops_t *, int);
+ uint_t (*tod_clear_watchdog_timer)(tod_ops_t *);
+ void (*tod_set_wake_alarm)(tod_ops_t *, int);
+ void (*tod_clear_wake_alarm)(tod_ops_t *);
struct tod_ops *tod_next;
};
@@ -58,6 +64,10 @@ extern char *tod_module_name;
#define TODOP_GET(top) ((top)->tod_get(top))
#define TODOP_SET(top, ts) ((top)->tod_set(top, ts))
+#define TODOP_SETWD(top, nsec) ((top)->tod_set_watchdog_timer(top, nsec))
+#define TODOP_CLRWD(top) ((top)->tod_clear_watchdog_timer(top))
+#define TODOP_SETWAKE(top, nsec) ((top)->tod_set_wake_alarm(top, nsec))
+#define TODOP_CLRWAKE(top) ((top)->tod_clear_wake_alarm(top))
#ifdef __cplusplus
}
diff --git a/usr/src/uts/i86pc/sys/machsystm.h b/usr/src/uts/i86pc/sys/machsystm.h
index fdaa21d218..7f5cb22437 100644
--- a/usr/src/uts/i86pc/sys/machsystm.h
+++ b/usr/src/uts/i86pc/sys/machsystm.h
@@ -56,6 +56,7 @@ extern "C" {
extern void mach_cpu_idle(void);
extern void mach_cpu_halt(char *);
extern int mach_cpu_start(cpu_t *, void *);
+extern int mach_cpuid_start(processorid_t, void *);
extern int Cpudelay;
extern void setcpudelay(void);
@@ -116,6 +117,7 @@ extern int mach_cpucontext_init(void);
extern void mach_cpucontext_fini(void);
extern void *mach_cpucontext_alloc(struct cpu *);
extern void mach_cpucontext_free(struct cpu *, void *, int);
+extern void rmp_gdt_init(rm_platter_t *);
extern uintptr_t hole_start, hole_end;
diff --git a/usr/src/uts/i86pc/sys/psm_common.h b/usr/src/uts/i86pc/sys/psm_common.h
index ef68cb9f07..b244f186c5 100644
--- a/usr/src/uts/i86pc/sys/psm_common.h
+++ b/usr/src/uts/i86pc/sys/psm_common.h
@@ -135,6 +135,8 @@ extern void acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq,
extern int acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin,
int *pci_irqp, iflag_t *intr_flagp);
+extern void acpi_restore_link_devices(void);
+
extern int acpi_poweroff(void);
extern void psm_set_elcr(int vecno, int val);
diff --git a/usr/src/uts/i86pc/sys/psm_types.h b/usr/src/uts/i86pc/sys/psm_types.h
index 70b49ed3dc..17e9db17b1 100644
--- a/usr/src/uts/i86pc/sys/psm_types.h
+++ b/usr/src/uts/i86pc/sys/psm_types.h
@@ -63,7 +63,32 @@ typedef enum psm_intr_op_e {
PSM_INTR_OP_APIC_TYPE /* 15. Returns APIC type */
} psm_intr_op_t;
-struct psm_ops {
+/*
+ * PSM_STATE definitions
+ */
+typedef enum psm_state_op_e {
+ PSM_STATE_ALLOC = 1,
+ PSM_STATE_FREE,
+ PSM_STATE_SAVE,
+ PSM_STATE_RESTORE
+} psm_state_op_t;
+
+typedef struct psm_state_req {
+ psm_state_op_t psr_cmd;
+ union psm_req {
+ /*
+ * PSM_STATE_ALLOC, PSM_STATE_FREE, PSM_STATE_SAVE,
+ * PSM_STATE_RESTORE all use the same struct,
+ * but union for later expansion
+ */
+ struct {
+ void *psr_state;
+ size_t psr_state_size;
+ } psm_state_req;
+ } req;
+} psm_state_request_t;
+
+struct psm_ops {
int (*psm_probe)(void);
void (*psm_softinit)(void);
@@ -80,7 +105,8 @@ struct psm_ops {
void (*psm_set_idlecpu)(processorid_t cpun);
void (*psm_unset_idlecpu)(processorid_t cpun);
-#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5)
+#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5) || \
+ defined(PSMI_1_6)
int (*psm_clkinit)(int hertz);
#else
void (*psm_clkinit)(int hertz);
@@ -91,14 +117,14 @@ struct psm_ops {
hrtime_t (*psm_gethrtime)(void);
processorid_t (*psm_get_next_processorid)(processorid_t cpu_id);
-#if defined(PSMI_1_5)
+#if defined(PSMI_1_5) || defined(PSMI_1_6)
int (*psm_cpu_start)(processorid_t cpun, caddr_t ctxt);
#else
void (*psm_cpu_start)(processorid_t cpun, caddr_t rm_code);
#endif
int (*psm_post_cpu_start)(void);
#if defined(PSMI_1_2) || defined(PSMI_1_3) || defined(PSMI_1_4) || \
- defined(PSMI_1_5)
+ defined(PSMI_1_5) || defined(PSMI_1_6)
void (*psm_shutdown)(int cmd, int fcn);
#else
void (*psm_shutdown)(void);
@@ -114,22 +140,26 @@ struct psm_ops {
#endif
void (*psm_notify_error)(int level, char *errmsg);
#if defined(PSMI_1_2) || defined(PSMI_1_3) || defined(PSMI_1_4) || \
- defined(PSMI_1_5)
+ defined(PSMI_1_5) || defined(PSMI_1_6)
void (*psm_notify_func)(int msg);
#endif
-#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5)
+#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5) || \
+ defined(PSMI_1_6)
void (*psm_timer_reprogram)(hrtime_t time);
void (*psm_timer_enable)(void);
void (*psm_timer_disable)(void);
void (*psm_post_cyclic_setup)(void *arg);
#endif
-#if defined(PSMI_1_4) || defined(PSMI_1_5)
+#if defined(PSMI_1_4) || defined(PSMI_1_5) || defined(PSMI_1_6)
void (*psm_preshutdown)(int cmd, int fcn);
#endif
-#if defined(PSMI_1_5)
+#if defined(PSMI_1_5) || defined(PSMI_1_6)
int (*psm_intr_ops)(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
psm_intr_op_t op, int *result);
#endif
+#if defined(PSMI_1_6)
+ int (*psm_state)(psm_state_request_t *request);
+#endif
};
@@ -153,6 +183,7 @@ struct psm_info {
#define PSM_INFO_VER01_3 0x8604
#define PSM_INFO_VER01_4 0x8605
#define PSM_INFO_VER01_5 0x8606
+#define PSM_INFO_VER01_6 0x8706
#define PSM_INFO_VER01_X (PSM_INFO_VER01_1 & 0xFFF0) /* ver 1.X */
/*
diff --git a/usr/src/uts/i86pc/sys/smp_impldefs.h b/usr/src/uts/i86pc/sys/smp_impldefs.h
index 266c0e43a1..6c45d46072 100644
--- a/usr/src/uts/i86pc/sys/smp_impldefs.h
+++ b/usr/src/uts/i86pc/sys/smp_impldefs.h
@@ -35,6 +35,7 @@
#include <sys/avintr.h>
#include <sys/pic.h>
#include <sys/xc_levels.h>
+#include <sys/psm_types.h>
#ifdef __cplusplus
extern "C" {
@@ -70,6 +71,7 @@ extern void (*psm_timer_reprogram)(hrtime_t); /* timer reprogram */
extern void (*psm_timer_enable)(void); /* timer enable */
extern void (*psm_timer_disable)(void); /* timer disable */
extern void (*psm_post_cyclic_setup)(void *arg); /* psm cyclic setup */
+extern int (*psm_state)(psm_state_request_t *); /* psm state save/restore */
extern int (*slvltovect)(int); /* ipl interrupt priority level */
extern int (*setlvl)(int, int *); /* set intr pri represented by vect */