Hi -

This (lengthly) email summarizes what I know to date about the Viking
MMU problem I reported on about a month ago.  Since it remains the
biggest headache in the way of DBRI audio, I'd like to get it resolved,
so anybody familiar with MMU caching problems, please read on.

Attached find a copy of /proc/cpuinfo, an exploit.c module, and a kernel
patch needed to compile exploit.o and insmod it.

The kernel patch introduces a flag into the iommu code that controls
whether get_free_page() will be used (the default), or
__get_free_page() instead.  As the attached kernel message log shows,
the program works correctly only if the flag is thrown and
__get_free_page() is used, which doesn't do a clear_page().

The clear_page(), I beleive, is critical.

The log shows how the module fails.  After getting the memory from
sparc_dvma_malloc(), the module writes a bunch of numbers into it,
then reads them back and prints them, one at a time.  The first number
works OK.  By the time we get to the second number, everything's
starting to read back as zeros.  If clear_page() isn't used, everything
works fine.

sparc_dvma_malloc() calls iommu_map_dma_area(), which gets a free page
and then adds an srmmu/Viking page entry for it (set_pte) as well as an
iommu entry for it (iopte_val).  I don't think the iommu entry enters
the equation - I've taken that assignment out and it doesn't affect the
behavior.

So now we've got a page with two srmmu/Viking mappings.  It appears
once in the kernel's big image of physical memory (that's what
get_free_page() returned - AND CLEARED), and another mapping at the
address requested in the DMA area.  It's a pointer to the second
mapping that gets returned to the module.

Here's my theory:  the first mapping is the one that gets cleared out
by clear_page() called from get_free_page().  But the writes to clear
this page get "stuck" in the cache.  The address returned to the module
is a different location in VM, so there's no cache hit, but they both
map to the same place in physical RAM.  Now the module writes into the
second mapping, and everything is OK until the first printk(), which
does enough stuff to flush the cached writes, which zeros out the
memory.

Of course, whoever wrote iommu_map_dma_area() knew that this might
happen after adding the second mapping, so that's why it does a
flush_cache_all(), which should make sure that the zeros get pushed
out before handing the address back

But look at flush_cache_all for the Viking! ...

        viking_flush_cache_all:
                WINDOW_FLUSH(%g4, %g5)
        viking_flush_cache_out:
                retl
                 nop

All it does is flush the user windows out onto the stack!  This can't
be right, can it?  So I think that flush_cache_all on the Viking
isn't flushing the cache.

Now, I've tried to patch up this code, to no avail, but I've got
an excuse - I don't have any Viking documentation, so I really don't
know what I'm doing...

Can anybody help?
-- 
                                        -bwb

                                        Brent Baccala
                                        [EMAIL PROTECTED]

-------------------------------------------------------------------------
To receive periodic news about what's happening at freesoft.org, send
email to "[EMAIL PROTECTED]", with "SUBSCRIBE" as the message, i.e:
                echo SUBSCRIBE | mail [EMAIL PROTECTED]
-------------------------------------------------------------------------

cpu             : Texas Instruments, Inc. - SuperSparc 50
fpu             : SuperSparc on-chip FPU
promlib         : Version 3 Revision 2
prom            : 2.15
type            : sun4m
ncpus probed    : 2
ncpus active    : 1
BogoMips        : 49.86
MMU type        : TI Viking
invall          : 0
invmm           : 0
invrnge         : 0
invpg           : 0
contexts        : 65536
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/pgtable.h>

/* Compile with:
 *
 * gcc -D__KERNEL__ -I/usr/src/linux-devel-new/include -Wall
 *   -Wstrict-prototypes -g -m32 -pipe -mno-fpu -fcall-used-g5 -fcall-used-g7
 *   -DMODULE -c -o exploit.o exploit.c
 *
 * Expected output is a list of 16 address:number pairs, where the numbers
 * range from 10 to 1f (hex).
 *
 * On my SS20, SuperSparc 50 CPU, TI Viking MMU, the first number is correct
 * (because the memory was examined before the first printk), and all
 * the rest of the numbers are zero.
 *
 * It doesn't appear to be a caching problem.  I've put flush_cache_all's
 * at all the places indicated by comments, to no avail.
 */

extern int iommu_erase_page;

void dvma_malloc_test()
{
        int *dma_area;
        __u32 dvma_addr;
        int i;

        dma_area = sparc_dvma_malloc(16 * sizeof(int),
                                     "DMA test area", &dvma_addr);

        for (i=0; i<16; i++) dma_area[i] = 16+i;

        for (i=0; i<16; i++) {
                int value = dma_area[i];
                printk("DMA test: %08x:%08x\n", dma_area+i, value);
        }

        printk("\nLet's try that again...\n");

        for (i=0; i<16; i++) dma_area[i] = 16+i;

        for (i=0; i<16; i++)
                printk("DMA test: %08x:%08x\n", dma_area+i, dma_area[i]);

        release_region(dma_area, 16 * sizeof(int));
}
int init_module(void)
{

        printk("Running test normal\n");
        dvma_malloc_test();

        printk("\nRunning test without the page erase\n");
        iommu_erase_page = 0;
        dvma_malloc_test();
        iommu_erase_page = 1;

        printk("\nRunning test normal again\n");
        dvma_malloc_test();

        return -EIO;
}

void cleanup_module(void)
{
}
*** arch/sparc/kernel/sparc_ksyms.c       1999/08/07 10:42:47     1.77.2.1
--- arch/sparc/kernel/sparc_ksyms.c       1999/08/12 22:46:24
***************
*** 121,126 ****
--- 121,129 ----
  EXPORT_SYMBOL(page_offset);
  EXPORT_SYMBOL(sparc_valid_addr_bitmap);
  
+ extern int iommu_erase_page;
+ EXPORT_SYMBOL(iommu_erase_page);
+ 
  #ifndef CONFIG_SUN4
  EXPORT_SYMBOL(stack_top);
  #endif
*** arch/sparc/mm/iommu.c     1999/05/07 17:03:34     1.10
--- arch/sparc/mm/iommu.c     1999/08/12 22:46:45
***************
*** 198,203 ****
--- 198,205 ----
  {
  }
  
+ int iommu_erase_page=1;
+ 
  #ifdef CONFIG_SBUS
  static void iommu_map_dma_area(unsigned long addr, int len)
  {
***************
*** 216,222 ****
        first = iopte;
        end = PAGE_ALIGN((addr + len));
        while(addr < end) {
!               page = get_free_page(GFP_KERNEL);
                if(!page) {
                        prom_printf("alloc_dvma: Cannot get a dvma page\n");
                        prom_halt();
--- 218,227 ----
        first = iopte;
        end = PAGE_ALIGN((addr + len));
        while(addr < end) {
!               if (iommu_erase_page)
!                       page = get_free_page(GFP_KERNEL);
!               else
!                       page = __get_free_page(GFP_KERNEL);
                if(!page) {
                        prom_printf("alloc_dvma: Cannot get a dvma page\n");
                        prom_halt();
Running test normal
DMA test: fff11000:00000010
DMA test: fff11004:00000000
DMA test: fff11008:00000000
DMA test: fff1100c:00000000
DMA test: fff11010:00000000
DMA test: fff11014:00000000
DMA test: fff11018:00000000
DMA test: fff1101c:00000000
DMA test: fff11020:00000000
DMA test: fff11024:00000000
DMA test: fff11028:00000000
DMA test: fff1102c:00000000
DMA test: fff11030:00000000
DMA test: fff11034:00000000
DMA test: fff11038:00000000
DMA test: fff1103c:00000000

Let's try that again...
DMA test: fff11000:00000010
DMA test: fff11004:00000011
DMA test: fff11008:00000012
DMA test: fff1100c:00000013
DMA test: fff11010:00000014
DMA test: fff11014:00000015
DMA test: fff11018:00000016
DMA test: fff1101c:00000017
DMA test: fff11020:00000018
DMA test: fff11024:00000019
DMA test: fff11028:0000001a
DMA test: fff1102c:0000001b
DMA test: fff11030:0000001c
DMA test: fff11034:0000001d
DMA test: fff11038:0000001e
DMA test: fff1103c:0000001f

Running test without the page erase
DMA test: fff12000:00000010
DMA test: fff12004:00000011
DMA test: fff12008:00000012
DMA test: fff1200c:00000013
DMA test: fff12010:00000014
DMA test: fff12014:00000015
DMA test: fff12018:00000016
DMA test: fff1201c:00000017
DMA test: fff12020:00000018
DMA test: fff12024:00000019
DMA test: fff12028:0000001a
DMA test: fff1202c:0000001b
DMA test: fff12030:0000001c
DMA test: fff12034:0000001d
DMA test: fff12038:0000001e
DMA test: fff1203c:0000001f

Let's try that again...
DMA test: fff12000:00000010
DMA test: fff12004:00000011
DMA test: fff12008:00000012
DMA test: fff1200c:00000013
DMA test: fff12010:00000014
DMA test: fff12014:00000015
DMA test: fff12018:00000016
DMA test: fff1201c:00000017
DMA test: fff12020:00000018
DMA test: fff12024:00000019
DMA test: fff12028:0000001a
DMA test: fff1202c:0000001b
DMA test: fff12030:0000001c
DMA test: fff12034:0000001d
DMA test: fff12038:0000001e
DMA test: fff1203c:0000001f

Running test normal again
DMA test: fff13000:00000010
DMA test: fff13004:00000000
DMA test: fff13008:00000000
DMA test: fff1300c:00000000
DMA test: fff13010:00000000
DMA test: fff13014:00000000
DMA test: fff13018:00000000
DMA test: fff1301c:00000000
DMA test: fff13020:00000000
DMA test: fff13024:00000000
DMA test: fff13028:00000000
DMA test: fff1302c:00000000
DMA test: fff13030:00000000
DMA test: fff13034:00000000
DMA test: fff13038:00000000
DMA test: fff1303c:00000000

Let's try that again...
DMA test: fff13000:00000010
DMA test: fff13004:00000011
DMA test: fff13008:00000012
DMA test: fff1300c:00000013
DMA test: fff13010:00000014
DMA test: fff13014:00000015
DMA test: fff13018:00000016
DMA test: fff1301c:00000017
DMA test: fff13020:00000018
DMA test: fff13024:00000019
DMA test: fff13028:0000001a
DMA test: fff1302c:0000001b
DMA test: fff13030:0000001c
DMA test: fff13034:0000001d
DMA test: fff13038:0000001e
DMA test: fff1303c:0000001f

Reply via email to