cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Participant
Participant
1,040 Views
Registered: ‎10-22-2018

How do I protect a region of memory in a standalone app?

Jump to solution

I want to create a memory protection scheme between the two CPUs on my Zynq ZC702. During each application's startup I set the TLB attributes for the other CPU's regions of memory to be invalid (all zeros) so that any attempt to read or write them will result in a data abort. This works to an extent, however it is still possible for a CPU to modify the TLB attribute table in memory, even in user mode. How can I ensure that the TLB attributes won't be modified once set appropriately? Or is there a better way to accomplish this?

EDIT: I looked at documentation for TrustZone, but that approach doesn't seem to cover what I'm trying to do. Both CPUs need to have some restrictions placed on them, so neither can view the entire DDR.

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
Highlighted
Participant
Participant
714 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

I went ahead and defined a 1KB L2 table in .mmu_tbl (in translation_table.S) plus an extra 3KB of filler to prevent anything important from being placed in that space and being affected when I make it read-only. I also aligned .mmu_tbl to 32KB instead of 16KB to ensure both tables fall in the same 1MB region.

L2TableForMMUTable:
	/* Each table entry occupies one 32-bit word and there are
	 * 256 entries, so the entire table takes up 1KB.
	 * Each entry covers a 4KB section.
	 */
/* Can't fill entries until MMUTable's address is known. Fill with zeros for now.
 * Fill additional 3KB with zeros to prevent other data from being made
 * read-only by the L2 entry that makes this table read-only.
 */
.rept	0x400	/* 1KB + 3KB filler */
.word	0
.endr

Then in boot.S I fill the MMU table as before and fill the L2 table with attributes 0x176 (S=b0 TEX=b101 AP=b11, C=b0, B=b1). These attributes will stay in effect for most of the 1MB region, with the exception of the 5 entries that will make the tables read-only with attributes 0x366, which I've done here:

protect_tables:
	/* Map L2 table to L1 entry */
	ldr r0, =TblBase
	ldr r1, =L2Base
	lsr r3, r0, #20			/* get section number where the MMU Table is held */
	lsl r3, r3, #2			/* multiply by 4 to get offset into MMU Table */
	add r3, r0, r3			/* address of entry in MMU table that covers itself */
	ldr r6, =0x1e1			/* Domain=b1111 */
	add r2, r1, r6			/* combine L2Base address with domain info to create the entry */
	str r2, [r3]			/* Write entry in L1 table */
	/* Set up L2 Table to protect MMU table and itself */
	and r3, r0, #0x000ff000 /* 4KB subsection where MMU table starts */
	lsr r3, r3, #10			/* offset into L2 Table */
	add r3, r1, r3			/* address of entry in L2 table that covers first 4KB of MMU table */
	ldr r6, =0x366			/* S=b0 TEX=b101 AP=b110, C=b0, B=b1 */
	add r2, r0, r6			/* combine address and attributes to create an entry */
	ldr r4, =5			/* loop control. Store 5 consecutive entries */
mmu_protection_loop:
	str r2, [r3]			/* write the entry to L2 table */
	add r3, r3, #4			/* next entry in the table */
	add r2, r2, #0x1000		/* next 4KB subsection */
	subs r4, r4, #1
	bgt mmu_protection_loop

I stepped through this code with the debugger and ensured the tables got the correct entries. In my program's main, I added a few tests.

// Test MMU protections
*(int*)0x20000000 = 0; *(int*)0x10028800 = 0x20015de6;

0x20000000 is an address that is made inaccessible by the L1 table. Attempting to modify it results in a data abort, which is the correct behavior.

The second test is writing to the entry in the L1 table that corresponds to address 0x20000000. However, this test fails; the program is able to modify the MMU table and remove the protections I set up, even though this address should be write-protected by the L2 table. I'm right back where I started. What am I doing wrong?

The read-only protection was only enforced after I updated the DACR to use client mode for domain 15. By default boot.S is setting manager mode for all domains. Then to prevent further modifications to DACR I changed CPSR's mode bits to user (0b10000) just before jumping into my program's main.

View solution in original post

0 Kudos
15 Replies
Highlighted
Scholar
Scholar
1,001 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

I digged into at that when you asked me in your previous post  Look at the 3 AP[] bits in the TLB entries, you can set memory regions as read only.

 

0 Kudos
Highlighted
Participant
Participant
995 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

I am already setting the AP bits to zero in this case. The problem isn't with setting up the protection, it's with ensuring the protection can't be removed.

Are you implying I set the TLB entry corresponding to the region in which the TLB itself lies? That would modify attributes for the entire first megabyte of application memory.

0 Kudos
Highlighted
Scholar
Scholar
982 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

If the MMU table is in a RO memory section that means it should not be possible to modify (write) to it.

As for the memory area size, it seems you are only using a single level - add another level and the granularity goes down to a few Ks

 

0 Kudos
Highlighted
Participant
Participant
965 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

If I do that, I also need to protect the memory in which the level 2 table resides. I would run into the same issue as before, where the region size of 4KB is still larger than the whole level 2 table, which is 1KB. I'm getting the feeling we're chasing down the wrong solution and we should be looking somewhere else entirely.

0 Kudos
Highlighted
Scholar
Scholar
956 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

You don't need to use a second for all 1st level entries, only for the entry where the MMU table is located.

0 Kudos
Highlighted
Participant
Participant
949 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

I don't think you understood my last reply. I can protect a memory region by setting attributes in the first level table, and I can protect the first level table by using four entries in a second level table corresponding to the location of the first level table. But then how do I protect the second level table?

Consider this example:

1MB region starting at 0x12300000 needs to be completely inaccessible.

L1 table is at 0x10040000 and is 16KB large

I set the value in the L1 table at 0x1004048C to 0x12300000 (attributes are zero)

There is an L2 table for the 1MB region in which the L1 table resides and is 1KB large

I set four values (4 * 4KB = 16KB) in this L2 table corresponding to the L1 table to make the L1 table read-only.

Now how do I protect this L2 table? It is 1KB in size and I can't set attributes for anything smaller than 4KB.

Like I said before, this seems way too convoluted to be the correct approach but I am finding absolutely no information anywhere else about how to do this.

0 Kudos
Highlighted
Scholar
Scholar
934 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

4k to protect 1K within.... Are you that short in memory? If so, then put code in the remaining 3K as code is only read... unless you are using self-modifying code.

As for the info, the MMU TLBs are described in details "ARM Architecture Refence Manual / ARMv7-A and ARMv7-R edition" document.

 

0 Kudos
Highlighted
Participant
Participant
917 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

I'm not short on memory, but I'm doubtful about this method. I'm trying to think of how I can programmatically determine where to put the L2 table such that the following 3KB don't contain anything important while also ensuring it is within the same 1MB as the L1 table so that both tables can have their attributes defined within the same L2 table (a total of 5 entries).

I suppose this could work, but it just seems way too convoluted. There has to be a more proper way to accomplish this that we're not seeing. Memory protection is such a common requirement so I can't believe how little information I am able to find on it.

0 Kudos
Highlighted
Scholar
Scholar
906 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

Use the linker script & sections to define where the tables must land in memory.

0 Kudos
Highlighted
Participant
Participant
868 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

I defined a region outside of app0's memory called mmu_table and assigned .mmu_tbl to it. However, during linking I get the error "address 0x10044000 of app0.elf section '.mmu_tbl' is not within region 'mmu_table'." So the linker is still trying to place the .mmu_tbl in application memory. What else do I have to change?

0 Kudos
Highlighted
Scholar
Scholar
837 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

A bit diificult to say what is the issue as you are not providing much info.  I assume your tables are defined in assembler so in the .s file you can define the 2 tables with:

 

  .section ".mmu_tbl"
  .align 14                    @ Level 1 must be aligned on multiple of 16384 (1<<14)
L1_tlb:
    ....
L2_tlb:
   ...

and in your likner script this is how you would force them to land in a specific memory area (the memory described in MEMORY by XXXX, alike XXXX : ORIGIN = 0x01000000, LENGTH = 0x0005000).

 

SECTIONS {
....
.mmu_tbl : { *(.mmu_tbl) *(.mmu_tbl.*) } > XXXX /* Memory section where the table must land */
....

 

0 Kudos
Highlighted
Participant
Participant
789 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

This is for the application running on CPU0. I modified my lscript.ld using the Summary editor; the section is called mmu_table and .mmu_tbl is assigned to it. I'm trying it without a L2 table for now just to make sure it does what I expect. It's still giving me the same linker error.

The resulting linker script source has:

MEMORY
{
ps7_ddr_0 : ORIGIN = 0x10000000, LENGTH = 0xFE00000
ps7_qspi_linear_0 : ORIGIN = 0xFC000000, LENGTH = 0x1000000
ps7_ram_0 : ORIGIN = 0x0, LENGTH = 0x30000
ps7_ram_1 : ORIGIN = 0xFFFF0000, LENGTH = 0xFE00
sharedMem : ORIGIN = 0x100000, LENGTH = 0x100000
mmu_table : ORIGIN = 0x1FE00000, LENGTH = 0x4000
}
.mmu_tbl (ALIGN(16384)) : {
__mmu_tbl_start = .;
*(.mmu_tbl)
__mmu_tbl_end = .;
} > mmu_table

I haven't manually defined the L1 table in the .mmu_tbl section. It appears to have been done for me in translation_table.S in the BSP.

	.globl  MMUTable

	.section .mmu_tbl,"a"

MMUTable:
...

Let me know if you need more info. I know the section addresses and sizes look strange. I'll get that all rearranged once this is sorted out.

0 Kudos
Highlighted
Scholar
Scholar
781 Views
Registered: ‎04-13-2015

Re: How do I protect a region of memory in a standalone app?

Jump to solution

@mberemand 

That should work.... unless there is one or more extra .mmu_tbl sections alsp declared in your project. Then the total size will exceed 16K explaining that error message. Try using a section name different from .mmu_tbl for your own table.

0 Kudos
Highlighted
Participant
Participant
760 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

There are no duplicate .mmu_tbl sections declared anywhere. I don't see anything in the default boot.S and translation_table.S files that would preclude me from simply assigning it to a new region in memory in the linker script. If I were to create my own section with my own table I wouldn't end up doing anything differently than what's already done, aside from what attributes are set.

0 Kudos
Highlighted
Participant
Participant
715 Views
Registered: ‎10-22-2018

Re: How do I protect a region of memory in a standalone app?

Jump to solution

I went ahead and defined a 1KB L2 table in .mmu_tbl (in translation_table.S) plus an extra 3KB of filler to prevent anything important from being placed in that space and being affected when I make it read-only. I also aligned .mmu_tbl to 32KB instead of 16KB to ensure both tables fall in the same 1MB region.

L2TableForMMUTable:
	/* Each table entry occupies one 32-bit word and there are
	 * 256 entries, so the entire table takes up 1KB.
	 * Each entry covers a 4KB section.
	 */
/* Can't fill entries until MMUTable's address is known. Fill with zeros for now.
 * Fill additional 3KB with zeros to prevent other data from being made
 * read-only by the L2 entry that makes this table read-only.
 */
.rept	0x400	/* 1KB + 3KB filler */
.word	0
.endr

Then in boot.S I fill the MMU table as before and fill the L2 table with attributes 0x176 (S=b0 TEX=b101 AP=b11, C=b0, B=b1). These attributes will stay in effect for most of the 1MB region, with the exception of the 5 entries that will make the tables read-only with attributes 0x366, which I've done here:

protect_tables:
	/* Map L2 table to L1 entry */
	ldr r0, =TblBase
	ldr r1, =L2Base
	lsr r3, r0, #20			/* get section number where the MMU Table is held */
	lsl r3, r3, #2			/* multiply by 4 to get offset into MMU Table */
	add r3, r0, r3			/* address of entry in MMU table that covers itself */
	ldr r6, =0x1e1			/* Domain=b1111 */
	add r2, r1, r6			/* combine L2Base address with domain info to create the entry */
	str r2, [r3]			/* Write entry in L1 table */
	/* Set up L2 Table to protect MMU table and itself */
	and r3, r0, #0x000ff000 /* 4KB subsection where MMU table starts */
	lsr r3, r3, #10			/* offset into L2 Table */
	add r3, r1, r3			/* address of entry in L2 table that covers first 4KB of MMU table */
	ldr r6, =0x366			/* S=b0 TEX=b101 AP=b110, C=b0, B=b1 */
	add r2, r0, r6			/* combine address and attributes to create an entry */
	ldr r4, =5			/* loop control. Store 5 consecutive entries */
mmu_protection_loop:
	str r2, [r3]			/* write the entry to L2 table */
	add r3, r3, #4			/* next entry in the table */
	add r2, r2, #0x1000		/* next 4KB subsection */
	subs r4, r4, #1
	bgt mmu_protection_loop

I stepped through this code with the debugger and ensured the tables got the correct entries. In my program's main, I added a few tests.

// Test MMU protections
*(int*)0x20000000 = 0; *(int*)0x10028800 = 0x20015de6;

0x20000000 is an address that is made inaccessible by the L1 table. Attempting to modify it results in a data abort, which is the correct behavior.

The second test is writing to the entry in the L1 table that corresponds to address 0x20000000. However, this test fails; the program is able to modify the MMU table and remove the protections I set up, even though this address should be write-protected by the L2 table. I'm right back where I started. What am I doing wrong?

The read-only protection was only enforced after I updated the DACR to use client mode for domain 15. By default boot.S is setting manager mode for all domains. Then to prevent further modifications to DACR I changed CPSR's mode bits to user (0b10000) just before jumping into my program's main.

View solution in original post

0 Kudos