12. MTRR(内存类型范围寄存器)控制

作者:

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  */