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 --]
next prev parent 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