diff options
Diffstat (limited to 'usr/src/uts/i86pc')
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, - ®_offset); + ®_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, - ®_offset); + PCI_REG_ADDR_M|PCI_REG_REL_M, + PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR, + ®_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)®, &length) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®, &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)®, &length) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®, &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, - ®len) != DDI_PROP_SUCCESS) { + DDI_PROP_DONTPASS, "reg", (int **)&drv_regp, + ®len) != 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 */ |