Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a989705c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6:
  [IA64] update memory attribute aliasing documentation & test cases
  [IA64] fail mmaps that span areas with incompatible attributes
  [IA64] allow WB /sys/.../legacy_mem mmaps
  [IA64] make ioremap avoid unsupported attributes
  [IA64] rename ioremap variables to match i386
  [IA64] relax per-cpu TLB requirement to DTC
  [IA64] remove per-cpu ia64_phys_stacked_size_p8
  [IA64] Fix example error injection program
  [IA64] Itanium MC Error Injection Tool: pal_mc_error_inject() interface
  [IA64] Itanium MC Error Injection Tool: Makefile changes
  [IA64] Itanium MC Error Injection Tool: Driver sysfs interface
  [IA64] Itanium MC Error Injection Tool: Doc and sample application
  [IA64] Itanium MC Error Injection Tool: Kernel configuration
parents 2d56d3c4 d2918253
Loading
Loading
Loading
Loading
+247 −0
Original line number Diff line number Diff line
/*
 * Exercise /dev/mem mmap cases that have been troublesome in the past
 *
 * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
 *	Bjorn Helgaas <bjorn.helgaas@hp.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

int sum;

int map_mem(char *path, off_t offset, size_t length, int touch)
{
	int fd, rc;
	void *addr;
	int *c;

	fd = open(path, O_RDWR);
	if (fd == -1) {
		perror(path);
		return -1;
	}

	addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
	if (addr == MAP_FAILED)
		return 1;

	if (touch) {
		c = (int *) addr;
		while (c < (int *) (offset + length))
			sum += *c++;
	}

	rc = munmap(addr, length);
	if (rc == -1) {
		perror("munmap");
		return -1;
	}

	close(fd);
	return 0;
}

int scan_sysfs(char *path, char *file, off_t offset, size_t length, int touch)
{
	struct dirent **namelist;
	char *name, *path2;
	int i, n, r, rc, result = 0;
	struct stat buf;

	n = scandir(path, &namelist, 0, alphasort);
	if (n < 0) {
		perror("scandir");
		return -1;
	}

	for (i = 0; i < n; i++) {
		name = namelist[i]->d_name;

		if (fnmatch(".", name, 0) == 0)
			goto skip;
		if (fnmatch("..", name, 0) == 0)
			goto skip;

		path2 = malloc(strlen(path) + strlen(name) + 3);
		strcpy(path2, path);
		strcat(path2, "/");
		strcat(path2, name);

		if (fnmatch(file, name, 0) == 0) {
			rc = map_mem(path2, offset, length, touch);
			if (rc == 0)
				fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable");
			else if (rc > 0)
				fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length);
			else {
				fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length);
				return rc;
			}
		} else {
			r = lstat(path2, &buf);
			if (r == 0 && S_ISDIR(buf.st_mode)) {
				rc = scan_sysfs(path2, file, offset, length, touch);
				if (rc < 0)
					return rc;
			}
		}

		result |= rc;
		free(path2);

skip:
		free(namelist[i]);
	}
	free(namelist);
	return rc;
}

char buf[1024];

int read_rom(char *path)
{
	int fd, rc;
	size_t size = 0;

	fd = open(path, O_RDWR);
	if (fd == -1) {
		perror(path);
		return -1;
	}

	rc = write(fd, "1", 2);
	if (rc <= 0) {
		perror("write");
		return -1;
	}

	do {
		rc = read(fd, buf, sizeof(buf));
		if (rc > 0)
			size += rc;
	} while (rc > 0);

	close(fd);
	return size;
}

int scan_rom(char *path, char *file)
{
	struct dirent **namelist;
	char *name, *path2;
	int i, n, r, rc, result = 0;
	struct stat buf;

	n = scandir(path, &namelist, 0, alphasort);
	if (n < 0) {
		perror("scandir");
		return -1;
	}

	for (i = 0; i < n; i++) {
		name = namelist[i]->d_name;

		if (fnmatch(".", name, 0) == 0)
			goto skip;
		if (fnmatch("..", name, 0) == 0)
			goto skip;

		path2 = malloc(strlen(path) + strlen(name) + 3);
		strcpy(path2, path);
		strcat(path2, "/");
		strcat(path2, name);

		if (fnmatch(file, name, 0) == 0) {
			rc = read_rom(path2);

			/*
			 * It's OK if the ROM is unreadable.  Maybe there
			 * is no ROM, or some other error ocurred.  The
			 * important thing is that no MCA happened.
			 */
			if (rc > 0)
				fprintf(stderr, "PASS: %s read %ld bytes\n", path2, rc);
			else {
				fprintf(stderr, "PASS: %s not readable\n", path2);
				return rc;
			}
		} else {
			r = lstat(path2, &buf);
			if (r == 0 && S_ISDIR(buf.st_mode)) {
				rc = scan_rom(path2, file);
				if (rc < 0)
					return rc;
			}
		}

		result |= rc;
		free(path2);

skip:
		free(namelist[i]);
	}
	free(namelist);
	return rc;
}

main()
{
	int rc;

	if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0)
		fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n");
	else
		fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n");

	/*
	 * It's not safe to blindly read the VGA frame buffer.  If you know
	 * how to poke the card the right way, it should respond, but it's
	 * not safe in general.  Many machines, e.g., Intel chipsets, cover
	 * up a non-responding card by just returning -1, but others will
	 * report the failure as a machine check.
	 */
	if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0)
		fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n");
	else
		fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n");

	if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0)
		fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n");
	else
		fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n");

	/*
	 * Often you can map all the individual pieces above (0-0xA0000,
	 * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole
	 * thing at once.  This is because the individual pieces use different
	 * attributes, and there's no single attribute supported over the
	 * whole region.
	 */
	rc = map_mem("/dev/mem", 0, 1024*1024, 0);
	if (rc == 0)
		fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n");
	else if (rc > 0)
		fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n");
	else
		fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n");

	scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1);
	scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0);
	scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1);
	scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0);

	scan_rom("/sys/devices", "rom");
}
+37 −34
Original line number Diff line number Diff line
@@ -112,16 +112,6 @@ POTENTIAL ATTRIBUTE ALIASING CASES

	The /dev/mem mmap constraints apply.

	However, since this is for mapping legacy MMIO space, WB access
	does not make sense.  This matters on machines without legacy
	VGA support: these machines may have WB memory for the entire
	first megabyte (or even the entire first granule).

	On these machines, we could mmap legacy_mem as WB, which would
	be safe in terms of attribute aliasing, but X has no way of
	knowing that it is accessing regular memory, not a frame buffer,
	so the kernel should fail the mmap rather than doing it with WB.

    read/write of /dev/mem

	This uses copy_from_user(), which implicitly uses a kernel
@@ -138,14 +128,20 @@ POTENTIAL ATTRIBUTE ALIASING CASES

    ioremap()

	This returns a kernel identity mapping for use inside the
	kernel.
	This returns a mapping for use inside the kernel.

	If the region is in kern_memmap, we should use the attribute
	specified there.  Otherwise, if the EFI memory map reports that
	the entire granule supports WB, we should use that (granules
	that are partially reserved or occupied by firmware do not appear
	in kern_memmap).  Otherwise, we should use a UC mapping.
	specified there.

	If the EFI memory map reports that the entire granule supports
	WB, we should use that (granules that are partially reserved
	or occupied by firmware do not appear in kern_memmap).

	If the granule contains non-WB memory, but we can cover the
	region safely with kernel page table mappings, we can use
	ioremap_page_range() as most other architectures do.

	Failing all of the above, we have to fall back to a UC mapping.

PAST PROBLEM CASES

@@ -158,7 +154,7 @@ PAST PROBLEM CASES
      succeed.  It may create either WB or UC user mappings, depending
      on whether the region is in kern_memmap or the EFI memory map.

    mmap of 0x0-0xA0000 /dev/mem by "hwinfo" on HP sx1000 with VGA enabled
    mmap of 0x0-0x9FFFF /dev/mem by "hwinfo" on HP sx1000 with VGA enabled

      See https://bugzilla.novell.com/show_bug.cgi?id=140858.

@@ -171,28 +167,25 @@ PAST PROBLEM CASES
      so it is safe to use WB mappings.

      The kernel VGA driver may ioremap the VGA frame buffer at 0xA0000,
      which will use a granule-sized UC mapping covering 0-0xFFFFF.  This
      granule covers some WB-only memory, but since UC is non-speculative,
      the processor will never generate an uncacheable reference to the
      WB-only areas unless the driver explicitly touches them.
      which uses a granule-sized UC mapping.  This granule will cover some
      WB-only memory, but since UC is non-speculative, the processor will
      never generate an uncacheable reference to the WB-only areas unless
      the driver explicitly touches them.

    mmap of 0x0-0xFFFFF legacy_mem by "X"

      If the EFI memory map reports this entire range as WB, there
      is no VGA MMIO hole, and the mmap should fail or be done with
      a WB mapping.
      If the EFI memory map reports that the entire range supports the
      same attributes, we can allow the mmap (and we will prefer WB if
      supported, as is the case with HP sx[12]000 machines with VGA
      disabled).

      There's no easy way for X to determine whether the 0xA0000-0xBFFFF
      region is a frame buffer or just memory, so I think it's best to
      just fail this mmap request rather than using a WB mapping.  As
      far as I know, there's no need to map legacy_mem with WB
      mappings.
      If EFI reports the range as partly WB and partly UC (as on sx[12]000
      machines with VGA enabled), we must fail the mmap because there's no
      safe attribute to use.

      Otherwise, a UC mapping of the entire region is probably safe.
      The VGA hole means the region will not be in kern_memmap.  The
      HP sx1000 chipset doesn't support UC access to the memory surrounding
      the VGA hole, but X doesn't need that area anyway and should not
      reference it.
      If EFI reports some of the range but not all (as on Intel firmware
      that doesn't report the VGA frame buffer at all), we should fail the
      mmap and force the user to map just the specific region of interest.

    mmap of 0xA0000-0xBFFFF legacy_mem by "X" on HP sx1000 with VGA disabled

@@ -202,6 +195,16 @@ PAST PROBLEM CASES
      This is a special case of the previous case, and the mmap should
      fail for the same reason as above.

    read of /sys/devices/.../rom

      For VGA devices, this may cause an ioremap() of 0xC0000.  This
      used to be done with a UC mapping, because the VGA frame buffer
      at 0xA0000 prevents use of a WB granule.  The UC mapping causes
      an MCA on HP sx[12]000 chipsets.

      We should use WB page table mappings to avoid covering the VGA
      frame buffer.

NOTES

    [1] SDM rev 2.2, vol 2, sec 4.4.1.
+1068 −0

File added.

Preview size limit exceeded, changes collapsed.

+10 −0
Original line number Diff line number Diff line
@@ -439,6 +439,16 @@ config IA64_PALINFO
	  To use this option, you have to ensure that the "/proc file system
	  support" (CONFIG_PROC_FS) is enabled, too.

config IA64_MC_ERR_INJECT
	tristate "MC error injection support"
	help
	  Selets whether support for MC error injection. By enabling the
	  support, kernel provide sysfs interface for user application to
	  call MC error injection PAL procedure to inject various errors.
	  This is a useful tool for MCA testing.

	  If you're unsure, do not select this option.

config SGI_SN
	def_bool y if (IA64_SGI_SN2 || IA64_GENERIC)

+1 −0
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ CONFIG_COMPAT=y
CONFIG_IA64_MCA_RECOVERY=y
CONFIG_PERFMON=y
CONFIG_IA64_PALINFO=y
# CONFIG_MC_ERR_INJECT is not set
CONFIG_SGI_SN=y
# CONFIG_IA64_ESI is not set

Loading