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(页面属性表)。
提示
在英特尔 P6 系列处理器(奔腾 Pro、奔腾 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 个英特尔风格的 MTRR。
Centaur C6 (WinChip) 有 8 个 MCR,允许写入合并。这些都支持。
VIA Cyrix III 和 VIA C3 CPU 提供 8 个英特尔风格的 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 [email protected]
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 [email protected]
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 */