First Push
This commit is contained in:
478
NVIDIA-Linux-x86_64-535.161.07-grid/kernel/nvidia/nv-pat.c
Normal file
478
NVIDIA-Linux-x86_64-535.161.07-grid/kernel/nvidia/nv-pat.c
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1999-2017 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#define __NO_VERSION__
|
||||
|
||||
#include "os-interface.h"
|
||||
#include "nv-linux.h"
|
||||
#include "nv-reg.h"
|
||||
#include "nv-pat.h"
|
||||
|
||||
int nv_pat_mode = NV_PAT_MODE_DISABLED;
|
||||
|
||||
#if defined(NV_ENABLE_PAT_SUPPORT)
|
||||
/*
|
||||
* Private PAT support for use by the NVIDIA driver. This is used on
|
||||
* kernels that do not modify the PAT to include a write-combining
|
||||
* entry.
|
||||
*
|
||||
* On kernels that have CONFIG_X86_PAT, the NVIDIA driver still checks that the
|
||||
* WC entry is as expected before using PAT.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_X86_PAT)
|
||||
#define NV_ENABLE_BUILTIN_PAT_SUPPORT 0
|
||||
#else
|
||||
#define NV_ENABLE_BUILTIN_PAT_SUPPORT 1
|
||||
#endif
|
||||
|
||||
|
||||
#define NV_READ_PAT_ENTRIES(pat1, pat2) rdmsr(0x277, (pat1), (pat2))
|
||||
#define NV_WRITE_PAT_ENTRIES(pat1, pat2) wrmsr(0x277, (pat1), (pat2))
|
||||
#define NV_PAT_ENTRY(pat, index) \
|
||||
(((pat) & (0xff << ((index)*8))) >> ((index)*8))
|
||||
|
||||
#if NV_ENABLE_BUILTIN_PAT_SUPPORT
|
||||
|
||||
static unsigned long orig_pat1, orig_pat2;
|
||||
|
||||
static inline void nv_disable_caches(unsigned long *cr4)
|
||||
{
|
||||
unsigned long cr0 = read_cr0();
|
||||
write_cr0(((cr0 & (0xdfffffff)) | 0x40000000));
|
||||
wbinvd();
|
||||
*cr4 = NV_READ_CR4();
|
||||
if (*cr4 & 0x80) NV_WRITE_CR4(*cr4 & ~0x80);
|
||||
__flush_tlb();
|
||||
}
|
||||
|
||||
static inline void nv_enable_caches(unsigned long cr4)
|
||||
{
|
||||
unsigned long cr0 = read_cr0();
|
||||
wbinvd();
|
||||
__flush_tlb();
|
||||
write_cr0((cr0 & 0x9fffffff));
|
||||
if (cr4 & 0x80) NV_WRITE_CR4(cr4);
|
||||
}
|
||||
|
||||
static void nv_setup_pat_entries(void *info)
|
||||
{
|
||||
unsigned long pat1, pat2, cr4;
|
||||
unsigned long eflags;
|
||||
|
||||
#if defined(NV_ENABLE_HOTPLUG_CPU)
|
||||
int cpu = (NvUPtr)info;
|
||||
if ((cpu != 0) && (cpu != (int)smp_processor_id()))
|
||||
return;
|
||||
#endif
|
||||
|
||||
NV_SAVE_FLAGS(eflags);
|
||||
NV_CLI();
|
||||
nv_disable_caches(&cr4);
|
||||
|
||||
NV_READ_PAT_ENTRIES(pat1, pat2);
|
||||
|
||||
pat1 &= 0xffff00ff;
|
||||
pat1 |= 0x00000100;
|
||||
|
||||
NV_WRITE_PAT_ENTRIES(pat1, pat2);
|
||||
|
||||
nv_enable_caches(cr4);
|
||||
NV_RESTORE_FLAGS(eflags);
|
||||
}
|
||||
|
||||
static void nv_restore_pat_entries(void *info)
|
||||
{
|
||||
unsigned long cr4;
|
||||
unsigned long eflags;
|
||||
|
||||
#if defined(NV_ENABLE_HOTPLUG_CPU)
|
||||
int cpu = (NvUPtr)info;
|
||||
if ((cpu != 0) && (cpu != (int)smp_processor_id()))
|
||||
return;
|
||||
#endif
|
||||
|
||||
NV_SAVE_FLAGS(eflags);
|
||||
NV_CLI();
|
||||
nv_disable_caches(&cr4);
|
||||
|
||||
NV_WRITE_PAT_ENTRIES(orig_pat1, orig_pat2);
|
||||
|
||||
nv_enable_caches(cr4);
|
||||
NV_RESTORE_FLAGS(eflags);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE 1:
|
||||
* Functions register_cpu_notifier(), unregister_cpu_notifier(),
|
||||
* macros register_hotcpu_notifier, register_hotcpu_notifier,
|
||||
* and CPU states CPU_DOWN_FAILED, CPU_DOWN_PREPARE
|
||||
* were removed by the following commit:
|
||||
* 2016 Dec 25: b272f732f888d4cf43c943a40c9aaa836f9b7431
|
||||
*
|
||||
* NV_REGISTER_CPU_NOTIFIER_PRESENT is true when
|
||||
* register_cpu_notifier() is present.
|
||||
*
|
||||
* The functions cpuhp_setup_state() and cpuhp_remove_state() should be
|
||||
* used as an alternative to register_cpu_notifier() and
|
||||
* unregister_cpu_notifier() functions. The following
|
||||
* commit introduced these functions as well as the enum cpuhp_state.
|
||||
* 2016 Feb 26: 5b7aa87e0482be768486e0c2277aa4122487eb9d
|
||||
*
|
||||
* NV_CPUHP_CPUHP_STATE_PRESENT is true when cpuhp_setup_state() is present.
|
||||
*
|
||||
* For kernels where both cpuhp_setup_state() and register_cpu_notifier()
|
||||
* are present, we still use register_cpu_notifier().
|
||||
*/
|
||||
|
||||
static int
|
||||
nvidia_cpu_teardown(unsigned int cpu)
|
||||
{
|
||||
#if defined(NV_ENABLE_HOTPLUG_CPU)
|
||||
unsigned int this_cpu = get_cpu();
|
||||
|
||||
if (this_cpu == cpu)
|
||||
nv_restore_pat_entries(NULL);
|
||||
else
|
||||
smp_call_function(nv_restore_pat_entries, &cpu, 1);
|
||||
|
||||
put_cpu();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvidia_cpu_online(unsigned int cpu)
|
||||
{
|
||||
#if defined(NV_ENABLE_HOTPLUG_CPU)
|
||||
unsigned int this_cpu = get_cpu();
|
||||
|
||||
if (this_cpu == cpu)
|
||||
nv_setup_pat_entries(NULL);
|
||||
else
|
||||
smp_call_function(nv_setup_pat_entries, &cpu, 1);
|
||||
|
||||
put_cpu();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nv_enable_builtin_pat_support(void)
|
||||
{
|
||||
unsigned long pat1, pat2;
|
||||
|
||||
NV_READ_PAT_ENTRIES(orig_pat1, orig_pat2);
|
||||
nv_printf(NV_DBG_SETUP, "saved orig pats as 0x%lx 0x%lx\n", orig_pat1, orig_pat2);
|
||||
|
||||
on_each_cpu(nv_setup_pat_entries, NULL, 1);
|
||||
|
||||
NV_READ_PAT_ENTRIES(pat1, pat2);
|
||||
nv_printf(NV_DBG_SETUP, "changed pats to 0x%lx 0x%lx\n", pat1, pat2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void nv_disable_builtin_pat_support(void)
|
||||
{
|
||||
unsigned long pat1, pat2;
|
||||
|
||||
on_each_cpu(nv_restore_pat_entries, NULL, 1);
|
||||
|
||||
nv_pat_mode = NV_PAT_MODE_DISABLED;
|
||||
|
||||
NV_READ_PAT_ENTRIES(pat1, pat2);
|
||||
nv_printf(NV_DBG_SETUP, "restored orig pats as 0x%lx 0x%lx\n", pat1, pat2);
|
||||
}
|
||||
|
||||
static int
|
||||
nvidia_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
|
||||
{
|
||||
/* CPU_DOWN_FAILED was added by the following commit
|
||||
* 2004 Oct 18: 71da3667be80d30121df3972caa0bf5684228379
|
||||
*
|
||||
* CPU_DOWN_PREPARE was added by the following commit
|
||||
* 2004 Oct 18: d13d28de21d913aacd3c91e76e307fa2eb7835d8
|
||||
*
|
||||
* We use one ifdef for both macros since they were added on the same day.
|
||||
*/
|
||||
#if defined(CPU_DOWN_FAILED)
|
||||
switch (action)
|
||||
{
|
||||
case CPU_DOWN_FAILED:
|
||||
case CPU_ONLINE:
|
||||
nvidia_cpu_online((NvUPtr)hcpu);
|
||||
break;
|
||||
case CPU_DOWN_PREPARE:
|
||||
nvidia_cpu_teardown((NvUPtr)hcpu);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* See NOTE 1.
|
||||
* In order to avoid warnings for unused variable when compiling against
|
||||
* kernel versions which include changes of commit id
|
||||
* b272f732f888d4cf43c943a40c9aaa836f9b7431, we have to protect declaration
|
||||
* of nv_hotcpu_nfb with #if.
|
||||
*
|
||||
* NV_REGISTER_CPU_NOTIFIER_PRESENT is checked before
|
||||
* NV_CPUHP_SETUP_STATE_PRESENT to avoid compilation warnings for unused
|
||||
* variable nvidia_pat_online for kernels where both
|
||||
* NV_REGISTER_CPU_NOTIFIER_PRESENT and NV_CPUHP_SETUP_STATE_PRESENT
|
||||
* are true.
|
||||
*/
|
||||
#if defined(NV_REGISTER_CPU_NOTIFIER_PRESENT) && defined(CONFIG_HOTPLUG_CPU)
|
||||
static struct notifier_block nv_hotcpu_nfb = {
|
||||
.notifier_call = nvidia_cpu_callback,
|
||||
.priority = 0
|
||||
};
|
||||
#elif defined(NV_CPUHP_SETUP_STATE_PRESENT)
|
||||
static enum cpuhp_state nvidia_pat_online;
|
||||
#endif
|
||||
|
||||
static int
|
||||
nvidia_register_cpu_hotplug_notifier(void)
|
||||
{
|
||||
int ret;
|
||||
/* See NOTE 1 */
|
||||
#if defined(NV_REGISTER_CPU_NOTIFIER_PRESENT) && defined(CONFIG_HOTPLUG_CPU)
|
||||
/* register_hotcpu_notiifer() returns 0 on success or -ENOENT on failure */
|
||||
ret = register_hotcpu_notifier(&nv_hotcpu_nfb);
|
||||
#elif defined(NV_CPUHP_SETUP_STATE_PRESENT)
|
||||
/*
|
||||
* cpuhp_setup_state() returns positive number on success when state is
|
||||
* CPUHP_AP_ONLINE_DYN. On failure, it returns a negative number.
|
||||
*/
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
|
||||
"nvidia/pat:online",
|
||||
nvidia_cpu_online,
|
||||
nvidia_cpu_teardown);
|
||||
if (ret < 0)
|
||||
{
|
||||
/*
|
||||
* If cpuhp_setup_state() fails, the cpuhp_remove_state()
|
||||
* should never be called. If it gets called, we might remove
|
||||
* some other state. Hence, explicitly set
|
||||
* nvidia_pat_online to zero. This will trigger a BUG()
|
||||
* in cpuhp_remove_state().
|
||||
*/
|
||||
nvidia_pat_online = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nvidia_pat_online = ret;
|
||||
}
|
||||
#else
|
||||
|
||||
/*
|
||||
* This function should be a no-op for kernels which
|
||||
* - do not have CONFIG_HOTPLUG_CPU enabled,
|
||||
* - do not have PAT support,
|
||||
* - do not have the cpuhp_setup_state() function.
|
||||
*
|
||||
* On such kernels, returning an error here would result in module init
|
||||
* failure. Hence, return 0 here.
|
||||
*/
|
||||
if (nv_pat_mode == NV_PAT_MODE_BUILTIN)
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
nv_disable_pat_support();
|
||||
nv_printf(NV_DBG_ERRORS,
|
||||
"NVRM: CPU hotplug notifier registration failed!\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nvidia_unregister_cpu_hotplug_notifier(void)
|
||||
{
|
||||
/* See NOTE 1 */
|
||||
#if defined(NV_REGISTER_CPU_NOTIFIER_PRESENT) && defined(CONFIG_HOTPLUG_CPU)
|
||||
unregister_hotcpu_notifier(&nv_hotcpu_nfb);
|
||||
#elif defined(NV_CPUHP_SETUP_STATE_PRESENT)
|
||||
cpuhp_remove_state(nvidia_pat_online);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#else /* NV_ENABLE_BUILTIN_PAT_SUPPORT */
|
||||
|
||||
static int nv_enable_builtin_pat_support(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static void nv_disable_builtin_pat_support(void)
|
||||
{
|
||||
}
|
||||
static int nvidia_register_cpu_hotplug_notifier(void)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
static void nvidia_unregister_cpu_hotplug_notifier(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* NV_ENABLE_BUILTIN_PAT_SUPPORT */
|
||||
|
||||
static int nv_determine_pat_mode(void)
|
||||
{
|
||||
unsigned int pat1, pat2, i;
|
||||
NvU8 PAT_WC_index;
|
||||
|
||||
if (!test_bit(X86_FEATURE_PAT,
|
||||
(volatile unsigned long *)&boot_cpu_data.x86_capability))
|
||||
{
|
||||
if ((boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) ||
|
||||
(boot_cpu_data.cpuid_level < 1) ||
|
||||
((cpuid_edx(1) & (1 << 16)) == 0) ||
|
||||
(boot_cpu_data.x86 != 6) || (boot_cpu_data.x86_model >= 15))
|
||||
{
|
||||
nv_printf(NV_DBG_ERRORS,
|
||||
"NVRM: CPU does not support the PAT.\n");
|
||||
return NV_PAT_MODE_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
NV_READ_PAT_ENTRIES(pat1, pat2);
|
||||
PAT_WC_index = 0xf;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (NV_PAT_ENTRY(pat1, i) == 0x01)
|
||||
{
|
||||
PAT_WC_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (NV_PAT_ENTRY(pat2, i) == 0x01)
|
||||
{
|
||||
PAT_WC_index = (i + 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (PAT_WC_index == 1)
|
||||
{
|
||||
return NV_PAT_MODE_KERNEL;
|
||||
}
|
||||
else if (PAT_WC_index != 0xf)
|
||||
{
|
||||
nv_printf(NV_DBG_ERRORS,
|
||||
"NVRM: PAT configuration unsupported.\n");
|
||||
return NV_PAT_MODE_DISABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NV_ENABLE_BUILTIN_PAT_SUPPORT
|
||||
return NV_PAT_MODE_BUILTIN;
|
||||
#else
|
||||
return NV_PAT_MODE_DISABLED;
|
||||
#endif /* NV_ENABLE_BUILTIN_PAT_SUPPORT */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int nv_enable_pat_support(void)
|
||||
{
|
||||
if (nv_pat_mode != NV_PAT_MODE_DISABLED)
|
||||
return 1;
|
||||
|
||||
nv_pat_mode = nv_determine_pat_mode();
|
||||
|
||||
switch (nv_pat_mode)
|
||||
{
|
||||
case NV_PAT_MODE_DISABLED:
|
||||
/* avoid the PAT if unavailable/unusable */
|
||||
return 0;
|
||||
case NV_PAT_MODE_KERNEL:
|
||||
/* inherit the kernel's PAT layout */
|
||||
return 1;
|
||||
case NV_PAT_MODE_BUILTIN:
|
||||
/* use builtin code to modify the PAT layout */
|
||||
break;
|
||||
}
|
||||
|
||||
return nv_enable_builtin_pat_support();
|
||||
}
|
||||
|
||||
void nv_disable_pat_support(void)
|
||||
{
|
||||
if (nv_pat_mode != NV_PAT_MODE_BUILTIN)
|
||||
return;
|
||||
|
||||
nv_disable_builtin_pat_support();
|
||||
}
|
||||
|
||||
int nv_init_pat_support(nvidia_stack_t *sp)
|
||||
{
|
||||
NV_STATUS status;
|
||||
NvU32 data;
|
||||
int disable_pat = 0;
|
||||
int ret = 0;
|
||||
|
||||
status = rm_read_registry_dword(sp, NULL,
|
||||
NV_USE_PAGE_ATTRIBUTE_TABLE, &data);
|
||||
if ((status == NV_OK) && ((int)data != ~0))
|
||||
{
|
||||
disable_pat = (data == 0);
|
||||
}
|
||||
|
||||
if (!disable_pat)
|
||||
{
|
||||
nv_enable_pat_support();
|
||||
if (nv_pat_mode == NV_PAT_MODE_BUILTIN)
|
||||
{
|
||||
ret = nvidia_register_cpu_hotplug_notifier();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nv_printf(NV_DBG_ERRORS,
|
||||
"NVRM: builtin PAT support disabled.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nv_teardown_pat_support(void)
|
||||
{
|
||||
if (nv_pat_mode == NV_PAT_MODE_BUILTIN)
|
||||
{
|
||||
nv_disable_pat_support();
|
||||
nvidia_unregister_cpu_hotplug_notifier();
|
||||
}
|
||||
}
|
||||
#endif /* defined(NV_ENABLE_PAT_SUPPORT) */
|
||||
Reference in New Issue
Block a user