1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include "system_priv.h" 8 9#include <arch/arch_ops.h> 10#include <arch/mp.h> 11#include <fbl/auto_call.h> 12#include <inttypes.h> 13#include <kernel/timer.h> 14#include <platform.h> 15#include <trace.h> 16#include <vm/vm_aspace.h> 17 18#include <arch/x86/feature.h> 19#include <arch/x86/bootstrap16.h> 20#include <arch/x86/acpi.h> 21extern "C" { 22#include <acpica/acpi.h> 23#include <acpica/accommon.h> 24#include <acpica/achware.h> 25} 26 27#define LOCAL_TRACE 0 28 29namespace { 30 31// This thread performs the work for suspend/resume. We use a separate thread 32// rather than the invoking thread to let us lean on the context switch code 33// path to persist all of the usermode thread state that is not saved on a plain 34// mode switch. 35zx_status_t suspend_thread(void* raw_arg) { 36 auto arg = reinterpret_cast<const zx_system_powerctl_arg_t*>(raw_arg); 37 uint8_t target_s_state = arg->acpi_transition_s_state.target_s_state; 38 uint8_t sleep_type_a = arg->acpi_transition_s_state.sleep_type_a; 39 uint8_t sleep_type_b = arg->acpi_transition_s_state.sleep_type_b; 40 41 // Acquire resources for suspend and resume if necessary. 42 fbl::RefPtr<VmAspace> temp_aspace; 43 x86_realmode_entry_data* bootstrap_data; 44 struct x86_realmode_entry_data_registers regs; 45 paddr_t bootstrap_ip; 46 zx_status_t status; 47 status = x86_bootstrap16_acquire(reinterpret_cast<uintptr_t>(_x86_suspend_wakeup), 48 &temp_aspace, 49 reinterpret_cast<void**>(&bootstrap_data), 50 &bootstrap_ip); 51 if (status != ZX_OK) { 52 return status; 53 } 54 auto bootstrap_cleanup = fbl::MakeAutoCall([&bootstrap_data]() { 55 x86_bootstrap16_release(bootstrap_data); 56 }); 57 58 // Setup our resume path 59 ACPI_TABLE_FACS* facs = nullptr; 60 ACPI_STATUS acpi_status = AcpiGetTable((char *)ACPI_SIG_FACS, 1, 61 reinterpret_cast<ACPI_TABLE_HEADER**>(&facs)); 62 if (acpi_status != AE_OK) { 63 return ZX_ERR_BAD_STATE; 64 } 65 acpi_status = AcpiHwSetFirmwareWakingVector(facs, bootstrap_ip, 0); 66 if (acpi_status != AE_OK) { 67 return ZX_ERR_BAD_STATE; 68 } 69 auto wake_vector_cleanup = fbl::MakeAutoCall([facs]() { 70 AcpiHwSetFirmwareWakingVector(facs, 0, 0); 71 }); 72 73 bootstrap_data->registers_ptr = reinterpret_cast<uintptr_t>(®s); 74 75 arch_disable_ints(); 76 77 // Save system state. 78 platform_suspend(); 79 arch_suspend(); 80 81 // Do the actual suspend 82 TRACEF("Entering x86_acpi_transition_s_state\n"); 83 acpi_status = x86_acpi_transition_s_state(®s, target_s_state, 84 sleep_type_a, sleep_type_b); 85 if (acpi_status != AE_OK) { 86 arch_enable_ints(); 87 TRACEF("x86_acpi_transition_s_state failed: %x\n", acpi_status); 88 return ZX_ERR_INTERNAL; 89 } 90 TRACEF("Left x86_acpi_transition_s_state\n"); 91 92 // If we're here, we've resumed and need to restore our CPU context 93 DEBUG_ASSERT(arch_ints_disabled()); 94 95 arch_resume(); 96 platform_resume(); 97 timer_thaw_percpu(); 98 99 DEBUG_ASSERT(arch_ints_disabled()); 100 arch_enable_ints(); 101 return ZX_OK; 102} 103 104zx_status_t x86_set_pkg_pl1(const zx_system_powerctl_arg_t* arg) { 105 if ((x86_microarch != X86_MICROARCH_INTEL_SANDY_BRIDGE) && 106 (x86_microarch != X86_MICROARCH_INTEL_SILVERMONT) && 107 (x86_microarch != X86_MICROARCH_INTEL_BROADWELL) && 108 (x86_microarch != X86_MICROARCH_INTEL_HASWELL) && 109 (x86_microarch != X86_MICROARCH_INTEL_SKYLAKE) && 110 (x86_microarch != X86_MICROARCH_INTEL_KABYLAKE)) { 111 return ZX_ERR_NOT_SUPPORTED; 112 } 113 114 uint32_t power_limit = arg->x86_power_limit.power_limit; 115 uint8_t clamp = arg->x86_power_limit.clamp; 116 uint8_t enable = arg->x86_power_limit.enable; 117 118 uint64_t u = read_msr(X86_MSR_RAPL_POWER_UNIT); 119 uint64_t v = read_msr(X86_MSR_PKG_POWER_LIMIT); 120 121 uint64_t pu = 1 << (u & 0xf); 122 123 // TODO(ZX-1429) time window is not currently supported 124 125 v &= ~0x7fff; 126 if (power_limit > 0) { 127 uint64_t n = (power_limit * pu / 1000); 128 if (n > 0x7fff) { 129 return ZX_ERR_INVALID_ARGS; 130 } 131 v |= n; 132 } else { 133 // set to default if 0 134 v |= read_msr(X86_MSR_PKG_POWER_INFO) & 0x7fff; 135 } 136 137 if (clamp) { 138 v |= X86_MSR_PKG_POWER_LIMIT_PL1_CLAMP; 139 } else { 140 v &= ~X86_MSR_PKG_POWER_LIMIT_PL1_CLAMP; 141 } 142 143 if (enable) { 144 v |= X86_MSR_PKG_POWER_LIMIT_PL1_ENABLE; 145 } else { 146 v &= ~X86_MSR_PKG_POWER_LIMIT_PL1_ENABLE; 147 } 148 149 write_msr(X86_MSR_PKG_POWER_LIMIT, v); 150 return ZX_OK; 151} 152 153zx_status_t acpi_transition_s_state(const zx_system_powerctl_arg_t* arg) { 154 uint8_t target_s_state = arg->acpi_transition_s_state.target_s_state; 155 uint8_t sleep_type_a = arg->acpi_transition_s_state.sleep_type_a; 156 uint8_t sleep_type_b = arg->acpi_transition_s_state.sleep_type_b; 157 if (target_s_state == 0 || target_s_state > 5) { 158 TRACEF("Bad S-state: S%u\n", target_s_state); 159 return ZX_ERR_INVALID_ARGS; 160 } 161 162 // If not a shutdown, ensure CPU 0 is the only cpu left running. 163 if (target_s_state != 5 && mp_get_online_mask() != cpu_num_to_mask(0)) { 164 TRACEF("Too many CPUs running for state S%u\n", target_s_state); 165 return ZX_ERR_BAD_STATE; 166 } 167 168 // Acquire resources for suspend and resume if necessary. 169 if (target_s_state < 5) { 170 // If we're not shutting down, prepare a resume path and execute the 171 // suspend on a separate thread (see comment on |suspend_thread()| for 172 // explanation). 173 thread_t* t = thread_create("suspend-thread", suspend_thread, 174 const_cast<zx_system_powerctl_arg_t*>(arg), 175 HIGHEST_PRIORITY); 176 if (!t) { 177 return ZX_ERR_NO_MEMORY; 178 } 179 180 thread_resume(t); 181 182 zx_status_t retcode; 183 zx_status_t status = thread_join(t, &retcode, ZX_TIME_INFINITE); 184 ASSERT(status == ZX_OK); 185 186 if (retcode != ZX_OK) { 187 return retcode; 188 } 189 } else { 190 struct x86_realmode_entry_data_registers regs; 191 192 DEBUG_ASSERT(target_s_state == 5); 193 arch_disable_ints(); 194 195 ACPI_STATUS acpi_status = x86_acpi_transition_s_state(®s, target_s_state, 196 sleep_type_a, sleep_type_b); 197 arch_enable_ints(); 198 if (acpi_status != AE_OK) { 199 return ZX_ERR_INTERNAL; 200 } 201 } 202 203 return ZX_OK; 204} 205 206} // namespace 207 208zx_status_t arch_system_powerctl(uint32_t cmd, const zx_system_powerctl_arg_t* arg) { 209 switch (cmd) { 210 case ZX_SYSTEM_POWERCTL_ACPI_TRANSITION_S_STATE: 211 return acpi_transition_s_state(arg); 212 case ZX_SYSTEM_POWERCTL_X86_SET_PKG_PL1: 213 return x86_set_pkg_pl1(arg); 214 default: 215 return ZX_ERR_NOT_SUPPORTED; 216 } 217} 218