1// Copyright 2017 The Fuchsia Authors
2// Copyright (c) 2016 Travis Geiselbrecht
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7
8#include <arch/x86/feature.h>
9#include <err.h>
10#include <inttypes.h>
11#include <kernel/auto_lock.h>
12#include <kernel/mp.h>
13#include <kernel/spinlock.h>
14#include <lib/console.h>
15#include <string.h>
16#include <zircon/compiler.h>
17
18static bool hwp_enabled = false;
19
20static SpinLock lock;
21
22static void hwp_enable_sync_task(void* ctx) {
23    // Enable HWP
24    write_msr(X86_MSR_IA32_PM_ENABLE, 1);
25
26    // 14.4.7 set minimum/maximum to values from capabilities for
27    // common case. hint=0x80 by default
28    uint64_t hwp_caps = read_msr(X86_MSR_IA32_HWP_CAPABILITIES);
29    uint64_t hwp_req = (0x80ull << 24) | ((hwp_caps & 0xff) << 8) | ((hwp_caps >> 24) & 0xff);
30    write_msr(X86_MSR_IA32_HWP_REQUEST, hwp_req);
31}
32
33static void hwp_enable(void) {
34    AutoSpinLockNoIrqSave guard(&lock);
35
36    if (hwp_enabled) {
37        return;
38    }
39
40    if (!x86_feature_test(X86_FEATURE_HWP)) {
41        printf("HWP not supported\n");
42        return;
43    }
44
45    mp_sync_exec(MP_IPI_TARGET_ALL, 0, hwp_enable_sync_task, nullptr);
46
47    hwp_enabled = true;
48}
49
50static void hwp_set_hint_sync_task(void* ctx) {
51    uint8_t hint = (unsigned long)ctx & 0xff;
52    uint64_t hwp_req = read_msr(X86_MSR_IA32_HWP_REQUEST) & ~(0xff << 24);
53    hwp_req |= (hint << 24);
54    hwp_req &= ~(0xffffffffull << 32);
55    write_msr(X86_MSR_IA32_HWP_REQUEST, hwp_req);
56}
57
58static void hwp_set_hint(unsigned long hint) {
59    AutoSpinLockNoIrqSave guard(&lock);
60
61    if (!hwp_enabled) {
62        printf("Enable HWP first\n");
63        return;
64    }
65    if (!x86_feature_test(X86_FEATURE_HWP_PREF)) {
66        printf("HWP hint not supported\n");
67        return;
68    }
69    mp_sync_exec(MP_IPI_TARGET_ALL, 0, hwp_set_hint_sync_task, (void*)hint);
70}
71
72static int cmd_hwp(int argc, const cmd_args* argv, uint32_t flags) {
73    if (argc < 2) {
74    notenoughargs:
75        printf("not enough arguments\n");
76    usage:
77        printf("usage:\n");
78        printf("%s enable\n", argv[0].str);
79        printf("%s hint <0-255>\n", argv[0].str);
80        return ZX_ERR_INTERNAL;
81    }
82
83    if (!strcmp(argv[1].str, "enable")) {
84        hwp_enable();
85    } else if (!strcmp(argv[1].str, "hint")) {
86        if (argc < 3) {
87            goto notenoughargs;
88        }
89        if (argv[2].u > 0xff) {
90            printf("hint must be between 0 (performance) and 255 (energy efficiency)!");
91            goto usage;
92        }
93        hwp_set_hint(argv[2].u);
94    } else {
95        printf("unknown command\n");
96        goto usage;
97    }
98
99    return ZX_OK;
100}
101
102STATIC_COMMAND_START
103STATIC_COMMAND("hwp", "hardware controlled performance states\n", &cmd_hwp)
104STATIC_COMMAND_END(hwp);
105