Linux MIPS Architecture development
 help / color / mirror / Atom feed
From: James Hogan <james.hogan@imgtec.com>
To: Manuel Lauss <manuel.lauss@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>,
	Leonid Yegoshin <leonid.yegoshin@imgtec.com>,
	Linux-MIPS <linux-mips@linux-mips.org>
Subject: Re: [PATCH 1/2] MIPS: c-r4k: Sync icache when it fills from dcache
Date: Fri, 22 Jan 2016 12:19:08 +0000	[thread overview]
Message-ID: <20160122121908.GG31243@jhogan-linux.le.imgtec.org> (raw)
In-Reply-To: <CAOLZvyGeAgMt1KbmQR7c96WWXNJLr89b8hNSi9SePtjUK5K5fg@mail.gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 2280 bytes --]

On Fri, Jan 22, 2016 at 01:06:14PM +0100, Manuel Lauss wrote:
> Hi James,
> 
> On Fri, Jan 22, 2016 at 11:58 AM, James Hogan <james.hogan@imgtec.com> wrote:
> > It is still necessary to handle icache coherency in flush_cache_range()
> > and copy_to_user_page() when the icache fills from the dcache, even
> > though the dcache does not need to be written back. However when this
> > handling was added in commit 2eaa7ec286db ("[MIPS] Handle I-cache
> > coherency in flush_cache_range()"), it did not do any icache flushing
> > when it fills from dcache.
> >
> > Therefore fix r4k_flush_cache_range() to run
> > local_r4k_flush_cache_range() without taking into account whether icache
> > fills from dcache, so that the icache coherency gets handled. Checks are
> > also added in local_r4k_flush_cache_range() so that the dcache blast
> > doesn't take place when icache fills from dcache.
> >
> > A test to mmap a page PROT_READ|PROT_WRITE, modify code in it, and
> > mprotect it to VM_READ|VM_EXEC (similar to case described in above
> > commit) can hit this case quite easily to verify the fix.
> >
> > A similar check was added in commit f8829caee311 ("[MIPS] Fix aliasing
> > bug in copy_to_user_page / copy_from_user_page"), so also fix
> > copy_to_user_page() similarly, to call flush_cache_page() without taking
> > into account whether icache fills from dcache, since flush_cache_page()
> > already takes that into account to avoid performing a dcache flush.
> >
> > Signed-off-by: James Hogan <james.hogan@imgtec.com>
> > Cc: Ralf Baechle <ralf@linux-mips.org>
> > Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com>
> > Cc: Manuel Lauss <manuel.lauss@gmail.com>
> > Cc: linux-mips@linux-mips.org
> > ---
> >  arch/mips/mm/c-r4k.c | 11 +++++++++--
> >  arch/mips/mm/init.c  |  2 +-
> >  2 files changed, 10 insertions(+), 3 deletions(-)
> 
> 
> I did some light testing on Alchemy and see no problems so far.
> If it matters:  Tested-by: Manuel Lauss <manuel.lauss@gmail.com>

Thanks Manuel.

FWIW, attached is the test program I mentioned, which hits the first
part of this patch (flush_cache_range) via mprotect(2) and checks if
icache seems to have been flushed (tested on mips64r6, but should be
portable).

Cheers
James

[-- Attachment #1.2: mprotect.c --]
[-- Type: text/x-c, Size: 6033 bytes --]

/*
 * Copyright (C) 2016 Imagination Technologies Ltd.
 * Author: James Hogan <james.hogan@imgtec.com>
 *
 * Test that mprotect keeps icache in sync.
 * I.e.
 * 1) mprotect of non-PROT_EXEC mmap() should sync icache.
 * 2) later mprotect RW->RX should sync icache.
 * 3) later mprotect RWX->RWX should sync icache.
 *
 * Linux man pages do not state what mprotect(2) does with caches.
 *
 * IRIX man pages suggest that its mprotect(2) causes cache flushing to take
 * place to allow for execution.
 *
 * The MIPS behaviour was fixed in Linux kernel commit 2eaa7ec286db ("[MIPS]
 * Handle I-cache coherency in flush_cache_range()"), to accomodate the MIPS16
 * dynamic linker which would perform data relocations in a page also containing
 * code, allocated with mmap VM_READ|VM_WRITE, and then use mprotect(2) to make
 * it executable. Without the fix, stale icache lines could be used.
 */
#ifndef __mips__
#error This test only supports MIPS
#endif

#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cachectl.h>
#include <sys/mman.h>
#include <unistd.h>

#define REG_ZERO	 0
#define REG_V0		 2
#define REG_RA		31

#define OPC_S		26
#define RS_S		21
#define RT_S		16
#define IMM_S		 0
#define FUNC_S		 0

#define SPECIAL		(000u << OPC_S)
#define ADDIU		(011u << OPC_S)
#define JALR		(SPECIAL | (011U << FUNC_S))

#define MOV_V0(SIMM)	(ADDIU | (REG_V0 << RT_S) | ((uint16_t)(SIMM) << IMM_S))

void usage(char *arg0, FILE *f)
{
	fprintf(f, "Usage: %s <options>:\n"
		   " -h            : show this help text\n"
		   " --[no-]loop1  : run/skip RW/RX mprotect loop (=run)\n"
		   " --[no-]loop2  : run/skip RWX mprotect loop (=skip)\n"
		   " --loops <num> : set number of loop iterations (=%u)\n",
		   arg0, 0x10000);
}

int main(int argc, char **argv)
{
	const int pagesize = getpagesize();
	uint32_t *buf;
	int (*func)(void);
	unsigned int i;
	int ret;
	bool loop1 = true;
	bool loop2 = false;
	unsigned int max = 0xffff;

	for (i = 1; i < argc; ++i) {
		if (!strcmp(argv[i], "--loop1")) {
			loop1 = true;
		} else if (!strcmp(argv[i], "--no-loop1")) {
			loop1 = false;
		} else if (!strcmp(argv[i], "--loop2")) {
			loop2 = true;
		} else if (!strcmp(argv[i], "--no-loop2")) {
			loop2 = false;
		} else if (!strcmp(argv[i], "--loops")) {
			++i;
			if (i >= argc) {
				fprintf(stderr,
					"--loops expects an argument\n\n");
				goto bad_usage;
			}
			max = atoi(argv[i]) - 1;
			if (max > 0xffff) {
				fprintf(stderr,
					"--loops must be >= 1 and <= %u\n\n",
					0x10000);
				goto bad_usage;
			}
		} else if (!strcmp(argv[i], "-h")) {
			usage(argv[0], stdout);
			return EXIT_SUCCESS;
		} else {
bad_usage:
			usage(argv[0], stderr);
			return EXIT_FAILURE;
		}
	}

	/* Map a page where we can generate some code. */
	buf = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
		   MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
	if (buf == MAP_FAILED) {
		perror("mmap");
		return EXIT_FAILURE;
	}
	func = (void *)buf;


	/*
	 * Write a simple function returning a value:
	 * jr     ra
	 *  addiu v0, zero, -1
	 */
	buf[0] = JALR | (REG_RA << RS_S);
	buf[1] = MOV_V0(0xffff);
	/*
	 * Flush cache immediately, so we can fail more gracefully if mprotect
	 * doesn't work.
	 */
	ret = cacheflush(buf, sizeof(buf[0]) * 2, BCACHE);
	if (ret < 0) {
		perror("cacheflush");
		return EXIT_FAILURE;
	}

	/* Check mprotect flushes icache. */

	/* Make function return 0. */
	buf[1] = MOV_V0(0);
	/* Make the page executable. */
	ret = mprotect(buf, pagesize, PROT_READ|PROT_EXEC);
	if (ret < 0) {
		perror("initial mprotect\n");
		return EXIT_FAILURE;
	}
	/* Check the return value. */
	ret = func();
	if (ret != 0) {
		fprintf(stderr,
			"%s:%u: First mprotect(2) PROT_READ|PROT_EXEC didn't sync icache, ret=%x\n",
			__FILE__, __LINE__, ret);
		return EXIT_FAILURE;
	}

	printf("Initial mprotect SUCCESS\n");

	if (loop1) {
		/* Lets test mprotect(RW), modify, mprotect(RX) a bit more. */
		for (i = 0; i <= max; ++i) {
			/* Make the page writable, non-executable. */
			ret = mprotect(buf, pagesize, PROT_READ|PROT_WRITE);
			if (ret < 0) {
				perror("mprotect PROT_READ|PROT_WRITE\n");
				return EXIT_FAILURE;
			}
			/* Make the function return (int16_t)i. */
			buf[1] = MOV_V0(i);
			/* Make the page executable. */
			ret = mprotect(buf, pagesize, PROT_READ|PROT_EXEC);
			if (ret < 0) {
				perror("mprotect PROT_READ|PROT_EXEC\n");
				return EXIT_FAILURE;
			}

			/* Check the return value. */
			ret = func();
			if (ret != (int16_t)i) {
				fprintf(stderr,
					"%s:%u: Looped mprotect(2) PROT_READ|PROT_EXEC didn't sync icache, ret=%x, expected %x\n",
					__FILE__, __LINE__, ret, (int16_t)i);
				return EXIT_FAILURE;
			}
		}

		printf("Looped { mprotect RW, modify, mprotect RX, test } SUCCESS\n");
	}

	/*
	 * This loop is disabled by default, because Linux detects that the
	 * flags haven't changed and does nothing.
	 */
	if (loop2) {
		/* Make the page writable AND executable. */
		ret = mprotect(buf, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC);
		if (ret < 0) {
			perror("initial mprotect PROT_READ|PROT_WRITE|PROT_EXEC\n");
			return EXIT_FAILURE;
		}

		/* Lets test modify while PROT_EXEC. */
		for (i = 0; i <= max; ++i) {
			/* Make the function return (int16_t)i. */
			buf[1] = MOV_V0(i);
			/* Remind kernel of executability. */
			ret = mprotect(buf, pagesize,
				       PROT_READ|PROT_WRITE|PROT_EXEC);
			if (ret < 0) {
				perror("mprotect PROT_READ|PROT_WRITE|PROT_EXEC\n");
				return EXIT_FAILURE;
			}

			/* Check the return value. */
			ret = func();
			if (ret != (int16_t)i) {
				/* This is expected under Linux, see above. */
				fprintf(stderr,
					"%s:%u: Looped mprotect(2) PROT_READ|PROT_WRITE|PROT_EXEC didn't sync icache, ret=%x, expected %x\n",
					__FILE__, __LINE__, ret, (int16_t)i);
				return EXIT_FAILURE;
			}
		}

		printf("Looped { modify, mprotect RWX, test } SUCCESS\n");
	}

	/* Clean up */
	ret = munmap(buf, pagesize);
	if (ret < 0) {
		perror("munmap\n");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  reply	other threads:[~2016-01-22 12:19 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-22 10:58 [PATCH 0/2] MIPS: I6400: Avoid dcache flushes James Hogan
2016-01-22 10:58 ` James Hogan
2016-01-22 10:58 ` [PATCH 1/2] MIPS: c-r4k: Sync icache when it fills from dcache James Hogan
2016-01-22 10:58   ` James Hogan
2016-01-22 12:06   ` Manuel Lauss
2016-01-22 12:19     ` James Hogan [this message]
2016-01-22 13:05       ` Joshua Kinard
2016-01-22 13:54         ` James Hogan
2016-01-22 14:03         ` Ralf Baechle
2016-01-22 14:30       ` Manuel Lauss
2016-01-22 15:02         ` James Hogan
2016-01-22 15:55           ` Manuel Lauss
2016-01-22 10:58 ` [PATCH 2/2] MIPS: I6400: Icache " James Hogan
2016-01-22 10:58   ` James Hogan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160122121908.GG31243@jhogan-linux.le.imgtec.org \
    --to=james.hogan@imgtec.com \
    --cc=leonid.yegoshin@imgtec.com \
    --cc=linux-mips@linux-mips.org \
    --cc=manuel.lauss@gmail.com \
    --cc=ralf@linux-mips.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox