12. MTRR (内存类型范围寄存器) 控制¶
- 作者:
Richard Gooch <rgooch@atnf.csiro.au> - 1999 年 6 月 3 日
Luis R. Rodriguez <mcgrof@do-not-panic.com> - 2015 年 4 月 9 日
12.1. 逐步淘汰 MTRR 的使用¶
在现代 x86 硬件上,MTRR 的使用已被 PAT 取代。 Linux 驱动程序直接使用 MTRR 现在已完全淘汰,设备驱动程序应结合 ioremap_wc() 使用 arch_phys_wc_add()
,以便在非 PAT 系统上使 MTRR 生效,而在启用 PAT 的系统上则无操作但同样有效。
即使 Linux 不直接使用 MTRR,一些 x86 平台固件可能仍然会在操作系统启动之前提前设置 MTRR。 他们这样做是因为一些平台固件可能仍然实现了对 MTRR 的访问,这些访问将由平台固件直接控制和处理。 平台使用 MTRR 的一个例子是通过使用 SMI 处理程序,一个例子可能是风扇控制,平台代码需要对一些风扇控制寄存器进行不可缓存的访问。 此类平台访问不需要任何操作系统 MTRR 代码,只需要 mtrr_type_lookup() 来确保任何操作系统特定的映射请求与平台 MTRR 设置对齐。 但是,如果 MTRR 仅由平台固件代码设置,并且操作系统不进行任何特定的 MTRR 映射请求,则 mtrr_type_lookup() 应始终返回 MTRR_TYPE_INVALID。
有关详细信息,请参阅 PAT (页属性表)。
提示
在 Intel P6 系列处理器(Pentium Pro、Pentium II 及更高版本)上,内存类型范围寄存器 (MTRR) 可用于控制处理器对内存范围的访问。 当您在 PCI 或 AGP 总线上有视频 (VGA) 卡时,这最有用。 启用写入合并允许总线写入传输在通过 PCI/AGP 总线突发之前合并为更大的传输。 这可以使图像写入操作的性能提高 2.5 倍或更多。
Cyrix 6x86、6x86MX 和 M II 处理器具有地址范围寄存器 (ARR),它们提供与 MTRR 类似的功能。 对于这些处理器,ARR 用于模拟 MTRR。
AMD K6-2(步进 8 及更高版本)和 K6-3 处理器有两个 MTRR。 这些都支持。 AMD Athlon 系列提供 8 个 Intel 风格的 MTRR。
Centaur C6 (WinChip) 具有 8 个 MCR,允许写入合并。 这些都支持。
VIA Cyrix III 和 VIA C3 CPU 提供 8 个 Intel 风格的 MTRR。
CONFIG_MTRR 选项创建一个 /proc/mtrr 文件,可用于操作您的 MTRR。 通常,X 服务器应使用此文件。 这应该有一个相当通用的接口,以便可以轻松支持其他处理器上的类似控制寄存器。
/proc/mtrr 有两个接口:一个是允许您读取和写入的 ASCII 接口。 另一个是 ioctl() 接口。 ASCII 接口适用于管理。 ioctl() 接口适用于 C 程序(即 X 服务器)。 这些接口在下面描述,带有示例命令和 C 代码。
12.2. 从 shell 读取 MTRR¶
% cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size= 128MB: write-back, count=1
reg01: base=0x08000000 ( 128MB), size= 64MB: write-back, count=1
从 C-shell 创建 MTRR
# echo "base=0xf8000000 size=0x400000 type=write-combining" >! /proc/mtrr
或者如果您使用 bash
# echo "base=0xf8000000 size=0x400000 type=write-combining" >| /proc/mtrr
以及由此产生的结果
% cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size= 128MB: write-back, count=1
reg01: base=0x08000000 ( 128MB), size= 64MB: write-back, count=1
reg02: base=0xf8000000 (3968MB), size= 4MB: write-combining, count=1
这是用于基地址为 0xf8000000 且大小为 4 兆字节的视频 RAM。 要找出您的基地址,您需要查看 X 服务器的输出,它会告诉您线性帧缓冲区地址在哪里。 您可能会得到的典型行是
(--) S3: PCI: 968 rev 0, Linear FB @ 0xf8000000
请注意,您应该只使用 X 服务器中的值,因为它可能会移动帧缓冲区基地址,因此您可以信任的唯一值是 X 服务器报告的值。
要找出帧缓冲区的大小(什么,您实际上不知道?),以下行将告诉您
(--) S3: videoram: 4096k
那是 4 兆字节,即 0x400000 字节(十六进制)。 正在为 XFree86 编写一个补丁,它将使此过程自动化:换句话说,X 服务器将使用 ioctl() 接口来操作 /proc/mtrr,因此用户不必做任何事情。 如果您使用商业 X 服务器,请游说您的供应商添加对 MTRR 的支持。
12.3. 创建重叠的 MTRR¶
%echo "base=0xfb000000 size=0x1000000 type=write-combining" >/proc/mtrr
%echo "base=0xfb000000 size=0x1000 type=uncachable" >/proc/mtrr
以及结果
% cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size= 64MB: write-back, count=1
reg01: base=0xfb000000 (4016MB), size= 16MB: write-combining, count=1
reg02: base=0xfb000000 (4016MB), size= 4kB: uncachable, count=1
一些卡(尤其是 Voodoo Graphics 板)需要将这个 4 kB 区域从区域的开头排除,因为它用于寄存器。
注意:只有当您创建的第一个区域是 type=write-combining 时,您才能创建 type=uncachable 区域。
12.4. 从 C-shel 中删除 MTRR¶
% echo "disable=2" >! /proc/mtrr
或者使用 bash
% echo "disable=2" >| /proc/mtrr
12.5. 使用 ioctl() 从 C 程序读取 MTRR¶
/* mtrr-show.c
Source file for mtrr-show (example program to show MTRRs using ioctl()'s)
Copyright (C) 1997-1998 Richard Gooch
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Richard Gooch may be reached by email at rgooch@atnf.csiro.au
The postal address is:
Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/
/*
This program will use an ioctl() on /proc/mtrr to show the current MTRR
settings. This is an alternative to reading /proc/mtrr.
Written by Richard Gooch 17-DEC-1997
Last updated by Richard Gooch 2-MAY-1998
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <asm/mtrr.h>
#define TRUE 1
#define FALSE 0
#define ERRSTRING strerror (errno)
static char *mtrr_strings[MTRR_NUM_TYPES] =
{
"uncachable", /* 0 */
"write-combining", /* 1 */
"?", /* 2 */
"?", /* 3 */
"write-through", /* 4 */
"write-protect", /* 5 */
"write-back", /* 6 */
};
int main ()
{
int fd;
struct mtrr_gentry gentry;
if ( ( fd = open ("/proc/mtrr", O_RDONLY, 0) ) == -1 )
{
if (errno == ENOENT)
{
fputs ("/proc/mtrr not found: not supported or you don't have a PPro?\n",
stderr);
exit (1);
}
fprintf (stderr, "Error opening /proc/mtrr\t%s\n", ERRSTRING);
exit (2);
}
for (gentry.regnum = 0; ioctl (fd, MTRRIOC_GET_ENTRY, &gentry) == 0;
++gentry.regnum)
{
if (gentry.size < 1)
{
fprintf (stderr, "Register: %u disabled\n", gentry.regnum);
continue;
}
fprintf (stderr, "Register: %u base: 0x%lx size: 0x%lx type: %s\n",
gentry.regnum, gentry.base, gentry.size,
mtrr_strings[gentry.type]);
}
if (errno == EINVAL) exit (0);
fprintf (stderr, "Error doing ioctl(2) on /dev/mtrr\t%s\n", ERRSTRING);
exit (3);
} /* End Function main */
12.6. 使用 ioctl() 从 C 程序创建 MTRR¶
/* mtrr-add.c
Source file for mtrr-add (example programme to add an MTRRs using ioctl())
Copyright (C) 1997-1998 Richard Gooch
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Richard Gooch may be reached by email at rgooch@atnf.csiro.au
The postal address is:
Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/
/*
This programme will use an ioctl() on /proc/mtrr to add an entry. The first
available mtrr is used. This is an alternative to writing /proc/mtrr.
Written by Richard Gooch 17-DEC-1997
Last updated by Richard Gooch 2-MAY-1998
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <asm/mtrr.h>
#define TRUE 1
#define FALSE 0
#define ERRSTRING strerror (errno)
static char *mtrr_strings[MTRR_NUM_TYPES] =
{
"uncachable", /* 0 */
"write-combining", /* 1 */
"?", /* 2 */
"?", /* 3 */
"write-through", /* 4 */
"write-protect", /* 5 */
"write-back", /* 6 */
};
int main (int argc, char **argv)
{
int fd;
struct mtrr_sentry sentry;
if (argc != 4)
{
fprintf (stderr, "Usage:\tmtrr-add base size type\n");
exit (1);
}
sentry.base = strtoul (argv[1], NULL, 0);
sentry.size = strtoul (argv[2], NULL, 0);
for (sentry.type = 0; sentry.type < MTRR_NUM_TYPES; ++sentry.type)
{
if (strcmp (argv[3], mtrr_strings[sentry.type]) == 0) break;
}
if (sentry.type >= MTRR_NUM_TYPES)
{
fprintf (stderr, "Illegal type: \"%s\"\n", argv[3]);
exit (2);
}
if ( ( fd = open ("/proc/mtrr", O_WRONLY, 0) ) == -1 )
{
if (errno == ENOENT)
{
fputs ("/proc/mtrr not found: not supported or you don't have a PPro?\n",
stderr);
exit (3);
}
fprintf (stderr, "Error opening /proc/mtrr\t%s\n", ERRSTRING);
exit (4);
}
if (ioctl (fd, MTRRIOC_ADD_ENTRY, &sentry) == -1)
{
fprintf (stderr, "Error doing ioctl(2) on /dev/mtrr\t%s\n", ERRSTRING);
exit (5);
}
fprintf (stderr, "Sleeping for 5 seconds so you can see the new entry\n");
sleep (5);
close (fd);
fputs ("I've just closed /proc/mtrr so now the new entry should be gone\n",
stderr);
} /* End Function main */