mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 08:38:38 +08:00
Misc changed to get the SAMA5 ELF configuration with address environments working
This commit is contained in:
parent
8c535f00dd
commit
1725946447
19 changed files with 180 additions and 132 deletions
|
@ -3009,7 +3009,7 @@ VxWorks provides the following comparable interface:
|
|||
<h3><a name="up_addrenv_destroy">4.4.2 <code>up_addrenv_destroy()</code></a></h3>
|
||||
<p><b>Function Prototype</b>:<p>
|
||||
<ul>
|
||||
<code>int up_addrenv_destroy(group_addrenv_t addrenv);</code>
|
||||
<code>int up_addrenv_destroy(group_addrenv_t *addrenv);</code>
|
||||
</ul>
|
||||
<p><b>Description</b>:</p>
|
||||
<ul>
|
||||
|
@ -3047,7 +3047,7 @@ VxWorks provides the following comparable interface:
|
|||
<h3><a name="up_addrenv_vdata">4.4.4 <code>up_addrenv_vdata()</code></a></h3>
|
||||
<p><b>Function Prototype</b>:<p>
|
||||
<ul>
|
||||
<code>int up_addrenv_vdata(FAR group_addrenv_t addrenv, size_t textsize, FAR void **vdata);</code>
|
||||
<code>int up_addrenv_vdata(FAR group_addrenv_t *addrenv, size_t textsize, FAR void **vdata);</code>
|
||||
</ul>
|
||||
<p><b>Description</b>:</p>
|
||||
<ul>
|
||||
|
@ -3068,7 +3068,7 @@ VxWorks provides the following comparable interface:
|
|||
<h3><a name="up_addrenv_select">4.4.5 <code>up_addrenv_select()</code></a></h3>
|
||||
<p><b>Function Prototype</b>:<p>
|
||||
<ul>
|
||||
<code>int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv);</code>
|
||||
<code>int up_addrenv_select(group_addrenv_t *addrenv, save_addrenv_t *oldenv);</code>
|
||||
</ul>
|
||||
<p><b>Description</b>:</p>
|
||||
<ul>
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include <nuttx/config.h>
|
||||
#ifndef __ASSEMBLY__
|
||||
# include <stdint.h>
|
||||
# include <nuttx/pgalloc.h>
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -91,6 +92,26 @@ do { \
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
#if CONFIG_MM_PGSIZE != 4096
|
||||
# error Only pages sizes of 4096 are currently supported (CONFIG_ARCH_ADDRENV)
|
||||
#endif
|
||||
|
||||
/* Convert 4KiB pages to 1MiB sections */
|
||||
|
||||
# define __PG2SECT_SHIFT (20 - MM_PGSHIFT)
|
||||
# define __PG2SECT_MASK ((1 << __PG2SECT_SHIFT) - 1)
|
||||
|
||||
# define ARCH_PG2SECT(p) (((p) + __PG2SECT_MASK) >> __PG2SECT_SHIFT)
|
||||
# define ARCH_SECT2PG(s) ((s) << __PG2SECT_SHIFT)
|
||||
|
||||
# define ARCH_TEXT_NSECTS ARCH_PG2SECT(CONFIG_ARCH_TEXT_NPAGES)
|
||||
# define ARCH_DATA_NSECTS ARCH_PG2SECT(CONFIG_ARCH_DATA_NPAGES)
|
||||
# define ARCH_HEAP_NSECTS ARCH_PG2SECT(CONFIG_ARCH_HEAP_NPAGES)
|
||||
# define ARCH_STACK_NSECTS ARCH_PG2SECT(CONFIG_ARCH_STACK_NPAGES)
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Inline functions
|
||||
****************************************************************************/
|
||||
|
@ -112,10 +133,10 @@ do { \
|
|||
|
||||
struct group_addrenv_s
|
||||
{
|
||||
FAR uint32_t *text[CONFIG_ARCH_TEXT_NPAGES];
|
||||
FAR uint32_t *data[CONFIG_ARCH_DATA_NPAGES];
|
||||
FAR uint32_t *text[ARCH_TEXT_NSECTS];
|
||||
FAR uint32_t *data[ARCH_DATA_NSECTS];
|
||||
#if 0 /* Not yet implemented */
|
||||
FAR uint32_t *heap[CONFIG_ARCH_HEAP_NPAGES];
|
||||
FAR uint32_t *heap[ARCH_HEAP_NSECTS];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -132,10 +153,10 @@ typedef struct group_addrenv_s group_addrenv_t;
|
|||
|
||||
struct save_addrenv_s
|
||||
{
|
||||
FAR uint32_t text[CONFIG_ARCH_TEXT_NPAGES];
|
||||
FAR uint32_t data[CONFIG_ARCH_DATA_NPAGES];
|
||||
FAR uint32_t text[ARCH_TEXT_NSECTS];
|
||||
FAR uint32_t data[ARCH_DATA_NSECTS];
|
||||
#if 0 /* Not yet implemented */
|
||||
FAR uint32_t heap[CONFIG_ARCH_HEAP_NPAGES];
|
||||
FAR uint32_t heap[ARCH_HEAP_NSECTS];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
# include <stdint.h>
|
||||
# include <arch/arch.h>
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -249,7 +250,7 @@ struct xcptcontext
|
|||
*/
|
||||
|
||||
#if 0 /* Not yet implemented */
|
||||
FAR uint32_t *stack[CONFIG_ARCH_STACK_NPAGES];
|
||||
FAR uint32_t *stack[ARCH_STACK_NSECTS];
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -74,7 +74,11 @@
|
|||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/addrenv.h>
|
||||
|
||||
#include <arch/arch.h>
|
||||
#include <arch/irq.h>
|
||||
|
||||
#include "mmu.h"
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
|
||||
|
@ -190,8 +194,7 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
ntextpages = MM_NPAGES(textsize);
|
||||
ndatapages = MM_NPAGES(datasize);
|
||||
|
||||
if (ntextpages > CONFIG_ARCH_TEXT_NPAGES ||
|
||||
ndatapages > CONFIG_ARCH_DATA_NPAGES)
|
||||
if (ntextpages > ARCH_TEXT_NSECTS || ndatapages > ARCH_DATA_NSECTS)
|
||||
{
|
||||
return -E2BIG;
|
||||
}
|
||||
|
@ -203,14 +206,14 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
|
||||
/* Allocate .text space pages */
|
||||
|
||||
vaddr = CONFIG_ARCH_TEXT_VADDR;
|
||||
mapped = 0;
|
||||
vaddr = CONFIG_ARCH_TEXT_VBASE;
|
||||
nmapped = 0;
|
||||
|
||||
for (i = 0; i < ntextpages; i++)
|
||||
{
|
||||
/* Allocate one physical page */
|
||||
|
||||
paddr = mm_pgalloc(1);
|
||||
paddr = mm_pgalloc(ARCH_SECT2PG(1));
|
||||
if (!paddr)
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
|
@ -224,15 +227,15 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
|
||||
flags = irqsave();
|
||||
l1save = mmu_l1_getentry(vaddr);
|
||||
set_l1_entry(ARCH_SCRATCH_VADDR, paddr);
|
||||
l2table = (FAR uint32_t *)ARCH_SCRATCH_VADDR;
|
||||
set_l1_entry(ARCH_SCRATCH_VBASE, paddr);
|
||||
l2table = (FAR uint32_t *)ARCH_SCRATCH_VBASE;
|
||||
|
||||
/* Initialize the page table */
|
||||
|
||||
memset(l2table, 0, ENTRIES_PER_L2TABLE * sizeof(uint32_t));
|
||||
for (j = 0; j < ENTRIES_PER_L2TABLE && nmapped < ntextsize; j++)
|
||||
for (j = 0; j < ENTRIES_PER_L2TABLE && nmapped < textsize; j++)
|
||||
{
|
||||
set_l2_entry(l2table, paddr, vaddr, MMU_ROMFLAGS);
|
||||
set_l2_entry(l2table, paddr, vaddr, MMU_L2_TEXTFLAGS);
|
||||
nmapped += MM_PGSIZE;
|
||||
paddr += MM_PGSIZE;
|
||||
vaddr += MM_PGSIZE;
|
||||
|
@ -240,20 +243,20 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
|
||||
/* Restore the original L1 page table entry */
|
||||
|
||||
mmu_l1_restore(ARCH_SCRATCH_VADDR, l1save);
|
||||
irqrestore();
|
||||
mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save);
|
||||
irqrestore(flags);
|
||||
}
|
||||
|
||||
/* Allocate .bss/.data space pages */
|
||||
|
||||
vaddr = CONFIG_ARCH_DATA_VADDR;
|
||||
mapped = 0;
|
||||
vaddr = CONFIG_ARCH_DATA_VBASE;
|
||||
nmapped = 0;
|
||||
|
||||
for (i = 0; i < ndatapages; i++)
|
||||
{
|
||||
/* Allocate one physical page */
|
||||
|
||||
paddr = mm_pgalloc(1);
|
||||
paddr = mm_pgalloc(ARCH_SECT2PG(1));
|
||||
if (!paddr)
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
|
@ -267,15 +270,15 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
|
||||
flags = irqsave();
|
||||
l1save = mmu_l1_getentry(vaddr);
|
||||
set_l1_entry(ARCH_SCRATCH_VADDR, paddr);
|
||||
l2table = (FAR uint32_t *)ARCH_SCRATCH_VADDR;
|
||||
set_l1_entry(ARCH_SCRATCH_VBASE, paddr);
|
||||
l2table = (FAR uint32_t *)ARCH_SCRATCH_VBASE;
|
||||
|
||||
/* Initialize the page table */
|
||||
|
||||
memset(l2table, 0, ENTRIES_PER_L2TABLE * sizeof(uint32_t));
|
||||
for (j = 0; j < ENTRIES_PER_L2TABLE && nmapped < ndatasize; j++)
|
||||
for (j = 0; j < ENTRIES_PER_L2TABLE && nmapped < datasize; j++)
|
||||
{
|
||||
set_l2_entry(l2table, paddr, vaddr, MMU_MEMFLAGS);
|
||||
set_l2_entry(l2table, paddr, vaddr, MMU_L2_DATAFLAGS);
|
||||
nmapped += MM_PGSIZE;
|
||||
paddr += MM_PGSIZE;
|
||||
vaddr += MM_PGSIZE;
|
||||
|
@ -283,8 +286,8 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
|
||||
/* Restore the original L1 page table entry */
|
||||
|
||||
mmu_l1_restore(ARCH_SCRATCH_VADDR, l1save);
|
||||
irqrestore();
|
||||
mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save);
|
||||
irqrestore(flags);
|
||||
}
|
||||
|
||||
/* Notice that no pages are yet allocated for the heap */
|
||||
|
@ -312,44 +315,44 @@ errout:
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_destroy(group_addrenv_t addrenv)
|
||||
int up_addrenv_destroy(FAR group_addrenv_t *addrenv)
|
||||
{
|
||||
uintptr_t vaddr;
|
||||
int i;
|
||||
|
||||
DEBUGASSERT(addrenv);
|
||||
|
||||
for (vaddr = CONFIG_ARCH_TEXT_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_TEXT_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_TEXT_VBASE, i = 0;
|
||||
i < ARCH_TEXT_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
mmu_l1_clrentry(vaddr);
|
||||
if (addrenv->text[i])
|
||||
{
|
||||
mm_pgfree((uintptr_t)addrenv->text[i], 1);
|
||||
mm_pgfree((uintptr_t)addrenv->text[i], ARCH_SECT2PG(1));
|
||||
}
|
||||
}
|
||||
|
||||
for (vaddr = CONFIG_ARCH_DATA_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_DATA_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_DATA_VBASE, i = 0;
|
||||
i < ARCH_DATA_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
mmu_l1_clrentry(vaddr);
|
||||
if (addrenv->data[i])
|
||||
{
|
||||
mm_pgfree((uintptr_t)addrenv->data[i], 1);
|
||||
mm_pgfree((uintptr_t)addrenv->data[i], ARCH_SECT2PG(1));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* Not yet implemented */
|
||||
for (vaddr = CONFIG_ARCH_HEAP_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_HEAP_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_HEAP_VBASE, i = 0;
|
||||
i < ARCH_HEAP_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
mmu_l1_clrentry(vaddr);
|
||||
if (addrenv->heap[i])
|
||||
{
|
||||
mm_pgfree((uintptr_t)addrenv->heap[i], 1);
|
||||
mm_pgfree((uintptr_t)addrenv->heap[i], ARCH_SECT2PG(1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -376,7 +379,7 @@ int up_addrenv_destroy(group_addrenv_t addrenv)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_vtext(FAR group_addrenv_t addrenv, FAR void **vtext)
|
||||
int up_addrenv_vtext(FAR group_addrenv_t *addrenv, FAR void **vtext)
|
||||
{
|
||||
/* Not much to do in this case */
|
||||
|
||||
|
@ -407,7 +410,7 @@ int up_addrenv_vtext(FAR group_addrenv_t addrenv, FAR void **vtext)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_vdata(FAR group_addrenv_t addrenv, uintptr_t textsize,
|
||||
int up_addrenv_vdata(FAR group_addrenv_t *addrenv, uintptr_t textsize,
|
||||
FAR void **vdata)
|
||||
{
|
||||
/* Not much to do in this case */
|
||||
|
@ -442,7 +445,8 @@ int up_addrenv_vdata(FAR group_addrenv_t addrenv, uintptr_t textsize,
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
||||
int up_addrenv_select(FAR const group_addrenv_t *addrenv,
|
||||
FAR save_addrenv_t *oldenv)
|
||||
{
|
||||
uintptr_t vaddr;
|
||||
uintptr_t paddr;
|
||||
|
@ -450,8 +454,8 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
|
||||
DEBUGASSERT(addrenv);
|
||||
|
||||
for (vaddr = CONFIG_ARCH_TEXT_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_TEXT_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_TEXT_VBASE, i = 0;
|
||||
i < ARCH_TEXT_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
/* Save the old L1 page table entry */
|
||||
|
@ -463,7 +467,7 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
|
||||
/* Set (or clear) the new page table entry */
|
||||
|
||||
paddr = (uintptr_t)addrenv->text[i]
|
||||
paddr = (uintptr_t)addrenv->text[i];
|
||||
if (paddr)
|
||||
{
|
||||
set_l1_entry(vaddr, paddr);
|
||||
|
@ -474,8 +478,8 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
}
|
||||
}
|
||||
|
||||
for (vaddr = CONFIG_ARCH_DATA_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_DATA_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_DATA_VBASE, i = 0;
|
||||
i < ARCH_DATA_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
/* Save the old L1 page table entry */
|
||||
|
@ -487,7 +491,7 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
|
||||
/* Set (or clear) the new page table entry */
|
||||
|
||||
paddr = (uintptr_t)addrenv->data[i]
|
||||
paddr = (uintptr_t)addrenv->data[i];
|
||||
if (paddr)
|
||||
{
|
||||
set_l1_entry(vaddr, paddr);
|
||||
|
@ -499,8 +503,8 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
}
|
||||
|
||||
#if 0 /* Not yet implemented */
|
||||
for (vaddr = CONFIG_ARCH_HEAP_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_HEAP_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_HEAP_VBASE, i = 0;
|
||||
i < ARCH_HEAP_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
/* Save the old L1 page table entry */
|
||||
|
@ -512,7 +516,7 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
|
||||
/* Set (or clear) the new page table entry */
|
||||
|
||||
paddr = (uintptr_t)addrenv->heap[i]
|
||||
paddr = (uintptr_t)addrenv->heap[i];
|
||||
if (paddr)
|
||||
{
|
||||
set_l1_entry(vaddr, paddr);
|
||||
|
@ -524,7 +528,6 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
}
|
||||
#endif
|
||||
|
||||
memset(addrenv, 0, sizeof(group_addrenv_t));
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -545,16 +548,15 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_restore(save_addrenv_t oldenv)
|
||||
int up_addrenv_restore(FAR const save_addrenv_t *oldenv)
|
||||
{
|
||||
uintptr_t vaddr;
|
||||
uintptr_t paddr;
|
||||
int i;
|
||||
|
||||
DEBUGASSERT(addrenv);
|
||||
DEBUGASSERT(oldenv);
|
||||
|
||||
for (vaddr = CONFIG_ARCH_TEXT_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_TEXT_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_TEXT_VBASE, i = 0;
|
||||
i < ARCH_TEXT_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
/* Restore the L1 page table entry */
|
||||
|
@ -562,8 +564,8 @@ int up_addrenv_restore(save_addrenv_t oldenv)
|
|||
mmu_l1_restore(vaddr, oldenv->text[i]);
|
||||
}
|
||||
|
||||
for (vaddr = CONFIG_ARCH_DATA_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_DATA_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_DATA_VBASE, i = 0;
|
||||
i < ARCH_DATA_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
/* Restore the L1 page table entry */
|
||||
|
@ -572,8 +574,8 @@ int up_addrenv_restore(save_addrenv_t oldenv)
|
|||
}
|
||||
|
||||
#if 0 /* Not yet implemented */
|
||||
for (vaddr = CONFIG_ARCH_HEAP_VADDR, i = 0;
|
||||
i < CONFIG_ARCH_HEAP_NPAGES;
|
||||
for (vaddr = CONFIG_ARCH_HEAP_VBASE, i = 0;
|
||||
i < ARCH_HEAP_NSECTS;
|
||||
vaddr += MM_PGSIZE, i++)
|
||||
{
|
||||
/* Restore the L1 page table entry */
|
||||
|
@ -582,7 +584,6 @@ int up_addrenv_restore(save_addrenv_t oldenv)
|
|||
}
|
||||
#endif
|
||||
|
||||
memset(addrenv, 0, sizeof(group_addrenv_t));
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ void mmu_l1_setentry(uint32_t paddr, uint32_t vaddr, uint32_t mmuflags)
|
|||
****************************************************************************/
|
||||
|
||||
#if !defined(CONFIG_ARCH_ROMPGTABLE) && defined(CONFIG_ARCH_ADDRENV)
|
||||
void mmu_l1_restore(uint32ptr_t vaddr, uint32_t l1entry)
|
||||
void mmu_l1_restore(uintptr_t vaddr, uint32_t l1entry)
|
||||
{
|
||||
uint32_t *l1table = (uint32_t*)PGTABLE_BASE_VADDR;
|
||||
uint32_t index = vaddr >> 20;
|
||||
|
|
|
@ -62,14 +62,14 @@
|
|||
************************************************************************************/
|
||||
/* Configuration ********************************************************************/
|
||||
|
||||
#ifdef CONFIG_PAGING
|
||||
#if defined(CONFIG_PAGING) || defined(CONFIG_ARCH_ADDRENV)
|
||||
|
||||
/* Sanity check -- we cannot be using a ROM page table and supporting on-
|
||||
* demand paging.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_ARCH_ROMPGTABLE
|
||||
# error "Cannot support both CONFIG_PAGING and CONFIG_ARCH_ROMPGTABLE"
|
||||
# error "Cannot support both CONFIG_PAGING/CONFIG_ARCH_ADDRENV and CONFIG_ARCH_ROMPGTABLE"
|
||||
#endif
|
||||
#endif /* CONFIG_PAGING */
|
||||
|
||||
|
@ -270,8 +270,8 @@
|
|||
#define PMD_PTE_NS (1 << 3) /* Bit 3: Non-secure bit */
|
||||
/* Bit 4: Should be zero (SBZ) */
|
||||
#define PMD_PTE_DOM_SHIFT (5) /* Bits 5-8: Domain */
|
||||
#define PMD_PTE_DOM_MASK (15 << PMD_PTE_DOMAIN_SHIFT)
|
||||
# define PMD_PTE_DOM(n) ((n) << PMD_PTE_DOMAIN_SHIFT)
|
||||
#define PMD_PTE_DOM_MASK (15 << PMD_PTE_DOM_SHIFT)
|
||||
# define PMD_PTE_DOM(n) ((n) << PMD_PTE_DOM_SHIFT)
|
||||
/* Bit 9: Not implemented */
|
||||
#define PMD_PTE_PADDR_MASK (0xfffffc00) /* Bits 10-31: Page table base address */
|
||||
|
||||
|
@ -525,7 +525,7 @@
|
|||
#define PTE_WRITE_THROUGH (PTE_C)
|
||||
#define PTE_WRITE_BACK (PTE_B | PTE_C)
|
||||
|
||||
/* Default MMU flags for RAM memory, IO, vector region
|
||||
/* Default MMU flags for RAM memory, IO, vector sections (level 1)
|
||||
*
|
||||
* REVISIT: Here we expect all threads to be running at PL1
|
||||
*/
|
||||
|
@ -540,8 +540,24 @@
|
|||
PMD_STRONGLY_ORDERED | PMD_SECT_DOM(0) | \
|
||||
PMD_SECT_XN)
|
||||
|
||||
#define MMU_L1_VECTORFLAGS (PMD_TYPE_PTE | PMD_PTE_PXN | PMD_PTE_DOM(0))
|
||||
#define MMU_L2_VECTORFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_RW1)
|
||||
/* MMU Flags for each type memory region (level 1 and 2) */
|
||||
|
||||
#define MMU_L1_TEXTFLAGS (PMD_TYPE_PTE | PMD_PTE_DOM(0))
|
||||
#define MMU_L2_TEXTFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_R1)
|
||||
|
||||
#define MMU_L1_DATAFLAGS (PMD_TYPE_PTE | PMD_PTE_PXN | PMD_PTE_DOM(0))
|
||||
#define MMU_L2_DATAFLAGS (PTE_TYPE_SMALL | PTE_WRITE_BACK | PTE_AP_RW1)
|
||||
#define MMU_L2_ALLOCFLAGS (PTE_TYPE_SMALL | PTE_WRITE_BACK | PTE_AP_RW1)
|
||||
|
||||
#define MMU_L1_PGTABFLAGS (PMD_TYPE_PTE | PMD_PTE_PXN | PTE_WRITE_THROUGH | \
|
||||
PMD_PTE_DOM(0))
|
||||
#define MMU_L2_PGTABFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_RW1)
|
||||
|
||||
#define MMU_L1_VECTORFLAGS (PMD_TYPE_PTE | PMD_PTE_PXN | PMD_PTE_DOM(0))
|
||||
|
||||
#define MMU_L2_VECTRWFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_RW1)
|
||||
#define MMU_L2_VECTROFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_R1)
|
||||
#define MMU_L2_VECTORFLAGS MMU_L2_VECTRWFLAGS
|
||||
|
||||
/* Mapped section size */
|
||||
|
||||
|
@ -579,7 +595,7 @@
|
|||
# undef PGTABLE_L2_VBASE
|
||||
# define PGTABLE_L2_VBASE (PGTABLE_BASE_VADDR+PGTABLE_L2_OFFSET)
|
||||
|
||||
#endif /* CONFIG_PAGING */
|
||||
#endif /* PGTABLE_BASE_VADDR */
|
||||
|
||||
/* MMU flags ************************************************************************/
|
||||
|
||||
|
@ -603,20 +619,6 @@
|
|||
|
||||
#define PG_L1_PADDRMASK PMD_SECT_PADDR_MASK
|
||||
|
||||
/* MMU Flags for each type memory region. */
|
||||
|
||||
#define MMU_L1_TEXTFLAGS (PMD_TYPE_PTE | PMD_PTE_DOM(0))
|
||||
#define MMU_L2_TEXTFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_R1)
|
||||
#define MMU_L1_DATAFLAGS (PMD_TYPE_PTE | PMD_PTE_PXN | PMD_PTE_DOM(0))
|
||||
#define MMU_L2_DATAFLAGS (PTE_TYPE_SMALL | PTE_WRITE_BACK | PTE_AP_RW1)
|
||||
#define MMU_L2_ALLOCFLAGS (PTE_TYPE_SMALL | PTE_WRITE_BACK | PTE_AP_RW1)
|
||||
#define MMU_L1_PGTABFLAGS (PMD_TYPE_PTE | PMD_PTE_PXN | PTE_WRITE_THROUGH | \
|
||||
PMD_PTE_DOM(0))
|
||||
#define MMU_L2_PGTABFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_RW1)
|
||||
|
||||
#define MMU_L2_VECTRWFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_RW1)
|
||||
#define MMU_L2_VECTROFLAGS (PTE_TYPE_SMALL | PTE_WRITE_THROUGH | PTE_AP_R1)
|
||||
|
||||
/* Addresses of Memory Regions ******************************************************/
|
||||
|
||||
/* We position the locked region PTEs at an offset into the first
|
||||
|
@ -1030,7 +1032,7 @@ struct section_mapping_s
|
|||
* ldr r1, =PG_L2_PGTABLE_PADDR <-- Physical address of L2 page table
|
||||
* ldr r2, =PG_PGTABLE_NPAGES <-- Total number of pages
|
||||
* ldr r3, =PG_PGTABLE_NPAGE1 <-- Number of pages in the first PTE
|
||||
* ldr r4, =MMU_L1_PGTABFLAGS <-- L1 MMU flags
|
||||
* ldr r4, =MMU_L1_PGTABFLAGS <-- L1 MMU flags
|
||||
* pg_l1span r0, r1, r2, r3, r4, r4
|
||||
*
|
||||
* Inputs (unmodified unless noted):
|
||||
|
@ -1352,11 +1354,11 @@ void mmu_l1_setentry(uint32_t paddr, uint32_t vaddr, uint32_t mmuflags);
|
|||
****************************************************************************/
|
||||
|
||||
#if !defined(CONFIG_ARCH_ROMPGTABLE) && defined(CONFIG_ARCH_ADDRENV)
|
||||
void mmu_l1_restore(uint32ptr_t vaddr, uint32_t l1entry);
|
||||
void mmu_l1_restore(uintptr_t vaddr, uint32_t l1entry);
|
||||
#endif
|
||||
|
||||
/************************************************************************************
|
||||
* Name: mmu_l1_clrentry(uint32ptr_t vaddr);
|
||||
* Name: mmu_l1_clrentry
|
||||
*
|
||||
* Description:
|
||||
* Unmap one L1 region by writing zero into the L1 page table entry and by
|
||||
|
|
|
@ -113,7 +113,7 @@ CHIP_CSRCS += sam_sckc.c sam_serial.c
|
|||
|
||||
# Configuration dependent C and assembly language files
|
||||
|
||||
ifneq ($(CONFIG_MM_PGALLOC),y)
|
||||
ifeq ($(CONFIG_MM_PGALLOC),y)
|
||||
CHIP_CSRCS += sam_pgalloc.c
|
||||
endif
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
#include <nuttx/arch.h>
|
||||
#include <nuttx/pgalloc.h>
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#ifdef CONFIG_MM_PGALLOC
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
@ -317,9 +317,9 @@ errout_with_irq:
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_destroy(group_addrenv_t addrenv)
|
||||
int up_addrenv_destroy(FAR group_addrenv_t *addrenv)
|
||||
{
|
||||
FAR struct z180_cbr_s *cbr = (FAR struct z180_cbr_s *)addrenv;
|
||||
FAR struct z180_cbr_s *cbr = (FAR struct z180_cbr_s *)*addrenv;
|
||||
|
||||
DEBUGASSERT(cbr);
|
||||
|
||||
|
@ -355,7 +355,7 @@ int up_addrenv_destroy(group_addrenv_t addrenv)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_vtext(FAR group_addrenv_t addrenv, FAR void **vtext)
|
||||
int up_addrenv_vtext(FAR group_addrenv_t *addrenv, FAR void **vtext)
|
||||
{
|
||||
return CONFIG_Z180_COMMON1AREA_VIRTBASE;
|
||||
}
|
||||
|
@ -382,7 +382,7 @@ int up_addrenv_vtext(FAR group_addrenv_t addrenv, FAR void **vtext)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_vdata(FAR group_addrenv_t addrenv, uintptr_t textsize,
|
||||
int up_addrenv_vdata(FAR group_addrenv_t *addrenv, uintptr_t textsize,
|
||||
FAR void **vdata)
|
||||
{
|
||||
return CONFIG_Z180_COMMON1AREA_VIRTBASE + textsize;
|
||||
|
@ -413,7 +413,8 @@ int up_addrenv_vdata(FAR group_addrenv_t addrenv, uintptr_t textsize,
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
||||
int up_addrenv_select(FAR const group_addrenv_t *addrenv,
|
||||
FAR save_addrenv_t *oldenv)
|
||||
{
|
||||
FAR struct z180_cbr_s *cbr = (FAR struct z180_cbr_s *)addrenv;
|
||||
irqstate_t flags;
|
||||
|
@ -449,9 +450,9 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_addrenv_restore(save_addrenv_t oldenv)
|
||||
int up_addrenv_restore(FAR const save_addrenv_t *oldenv)
|
||||
{
|
||||
outp(Z180_MMU_CBR, (uint8_t)oldenv);
|
||||
outp(Z180_MMU_CBR, (uint8_t)*oldenv);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -301,7 +301,7 @@ int elf_addrenv_alloc(FAR struct elf_loadinfo_s *loadinfo, size_t textsize,
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
# define elf_addrenv_select(l) up_addrenv_select((l)->addrenv, &(l)->oldenv)
|
||||
# define elf_addrenv_select(l) up_addrenv_select(&(l)->addrenv, &(l)->oldenv)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -319,7 +319,7 @@ int elf_addrenv_alloc(FAR struct elf_loadinfo_s *loadinfo, size_t textsize,
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
# define elf_addrenv_restore(l) up_addrenv_restore((l)->oldenv)
|
||||
# define elf_addrenv_restore(l) up_addrenv_restore(&(l)->oldenv)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
@ -109,21 +109,21 @@ int elf_addrenv_alloc(FAR struct elf_loadinfo_s *loadinfo, size_t textsize,
|
|||
* selected.
|
||||
*/
|
||||
|
||||
ret = up_addrenv_vtext(loadinfo->addrenv, &vtext);
|
||||
ret = up_addrenv_vtext(&loadinfo->addrenv, &vtext);
|
||||
if (ret < 0)
|
||||
{
|
||||
bdbg("ERROR: up_addrenv_vtext failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = up_addrenv_vdata(loadinfo->addrenv, textsize, &vdata);
|
||||
ret = up_addrenv_vdata(&loadinfo->addrenv, textsize, &vdata);
|
||||
if (ret < 0)
|
||||
{
|
||||
bdbg("ERROR: up_adup_addrenv_vdatadrenv_vtext failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
loadinfo->textalloc = (uintptr_t)vaddr;
|
||||
loadinfo->textalloc = (uintptr_t)vtext;
|
||||
loadinfo->dataalloc = (uintptr_t)vdata;
|
||||
return OK;
|
||||
#else
|
||||
|
@ -163,21 +163,13 @@ void elf_addrenv_free(FAR struct elf_loadinfo_s *loadinfo)
|
|||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
int ret;
|
||||
|
||||
/* Free the address environemnt */
|
||||
/* Free the address environment */
|
||||
|
||||
ret = up_addrenv_destroy(loadinfo->addrenv);
|
||||
ret = up_addrenv_destroy(&loadinfo->addrenv);
|
||||
if (ret < 0)
|
||||
{
|
||||
bdbg("ERROR: up_addrenv_destroy failed: %d\n", ret);
|
||||
}
|
||||
|
||||
/* Clear out all indications of the allocated address environment */
|
||||
|
||||
loadinfo->textalloc = 0;
|
||||
loadinfo->dataalloc = 0;
|
||||
loadinfo->textsize = 0;
|
||||
loadinfo->datasize = 0;
|
||||
loadinfo->addrenv = 0;
|
||||
#else
|
||||
/* If there is an allocation for the ELF image, free it */
|
||||
|
||||
|
@ -185,10 +177,12 @@ void elf_addrenv_free(FAR struct elf_loadinfo_s *loadinfo)
|
|||
{
|
||||
kufree((FAR void *)loadinfo->textalloc);
|
||||
}
|
||||
|
||||
loadinfo->textalloc = 0;
|
||||
loadinfo->dataalloc = 0;
|
||||
loadinfo->textsize = 0;
|
||||
loadinfo->datasize = 0;
|
||||
#endif
|
||||
|
||||
/* Clear out all indications of the allocated address environment */
|
||||
|
||||
loadinfo->textalloc = 0;
|
||||
loadinfo->dataalloc = 0;
|
||||
loadinfo->textsize = 0;
|
||||
loadinfo->datasize = 0;
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ int nxflat_addrenv_alloc(FAR struct nxflat_loadinfo_s *loadinfo, size_t envsize)
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
# define nxflat_addrenv_select(l) up_addrenv_select((l)->addrenv, &(l)->oldenv)
|
||||
# define nxflat_addrenv_select(l) up_addrenv_select(&(l)->addrenv, &(l)->oldenv)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -108,7 +108,7 @@ int nxflat_addrenv_alloc(FAR struct nxflat_loadinfo_s *loadinfo, size_t envsize)
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
# define nxflat_addrenv_restore(l) up_addrenv_restore((l)->oldenv)
|
||||
# define nxflat_addrenv_restore(l) up_addrenv_restore(&(l)->oldenv)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
@ -118,7 +118,7 @@ int nxflat_addrenv_alloc(FAR struct nxflat_loadinfo_s *loadinfo, size_t envsize)
|
|||
* selected.
|
||||
*/
|
||||
|
||||
ret = up_addrenv_vdata(loadinfo->addrenv, 0, &vdata);
|
||||
ret = up_addrenv_vdata(&loadinfo->addrenv, 0, &vdata);
|
||||
if (ret < 0)
|
||||
{
|
||||
bdbg("ERROR: up_addrenv_vdata failed: %d\n", ret);
|
||||
|
@ -153,7 +153,7 @@ int nxflat_addrenv_alloc(FAR struct nxflat_loadinfo_s *loadinfo, size_t envsize)
|
|||
return OK;
|
||||
|
||||
errout_with_addrenv:
|
||||
(void)up_addrenv_destroy(loadinfo->addrenv);
|
||||
(void)up_addrenv_destroy(&loadinfo->addrenv);
|
||||
loadinfo->addrenv = 0;
|
||||
|
||||
errout_with_dspace:
|
||||
|
|
|
@ -3684,6 +3684,20 @@ Configurations
|
|||
the warning in the section "Information Common to All Configurations"
|
||||
for further information.
|
||||
|
||||
3. This configuration currently has Cortex-A address environments selected for testing. With this option, the MMU is used to create a custom address environment for each ELF program. This option can be disabled in which case the ELF programs will simply execute out normal memory allocated from the heap. To disable this feature:
|
||||
|
||||
System Type -> Architecture Options
|
||||
CONFIG_ARCH_ADDRENV=n : Disable address environment support
|
||||
|
||||
Memory Management
|
||||
CONFIG_GRAN=n : Disable the granule allocator
|
||||
CONFIG_MM_PGALLOC=n : Disable the page allocator
|
||||
|
||||
STATUS:
|
||||
2014-8024: This configuration works with the address environment
|
||||
option disable.
|
||||
2014-8-25: But still does not even build successfully with the
|
||||
address environment option enabled.
|
||||
nsh:
|
||||
|
||||
This configuration directory provide the NuttShell (NSH). This is a
|
||||
|
|
|
@ -258,7 +258,15 @@ CONFIG_ARCH_HAVE_VFORK=y
|
|||
CONFIG_ARCH_HAVE_MMU=y
|
||||
CONFIG_ARCH_NAND_HWECC=y
|
||||
# CONFIG_ARCH_HAVE_EXTCLK is not set
|
||||
# CONFIG_ARCH_ADDRENV is not set
|
||||
CONFIG_ARCH_ADDRENV=y
|
||||
CONFIG_ARCH_TEXT_VBASE=0x80000000
|
||||
CONFIG_ARCH_DATA_VBASE=0x80100000
|
||||
CONFIG_ARCH_HEAP_VBASE=0x80200000
|
||||
CONFIG_ARCH_STACK_VBASE=0x80300000
|
||||
CONFIG_ARCH_TEXT_NPAGES=256
|
||||
CONFIG_ARCH_DATA_NPAGES=256
|
||||
CONFIG_ARCH_HEAP_NPAGES=256
|
||||
CONFIG_ARCH_STACK_NPAGES=256
|
||||
# CONFIG_PAGING is not set
|
||||
# CONFIG_ARCH_IRQPRIO is not set
|
||||
CONFIG_ARCH_STACKDUMP=y
|
||||
|
@ -586,8 +594,11 @@ CONFIG_FS_ROMFS=y
|
|||
# CONFIG_MM_SMALL is not set
|
||||
CONFIG_MM_REGIONS=1
|
||||
# CONFIG_ARCH_HAVE_HEAP2 is not set
|
||||
# CONFIG_GRAN is not set
|
||||
# CONFIG_MM_PGALLOC is not set
|
||||
CONFIG_GRAN=y
|
||||
CONFIG_GRAN_SINGLE=y
|
||||
# CONFIG_GRAN_INTR is not set
|
||||
CONFIG_MM_PGALLOC=y
|
||||
CONFIG_MM_PGSIZE=4096
|
||||
|
||||
#
|
||||
# Audio Support
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
|
||||
/* A single page scratch region used for temporary mappings */
|
||||
|
||||
#define ARCH_SCRATCH_VADDR (CONFIG_ARCH_STACK_VBASE + CONFIG_ARCH_STACK_SIZE)
|
||||
#define ARCH_SCRATCH_VBASE (CONFIG_ARCH_STACK_VBASE + CONFIG_ARCH_STACK_SIZE)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
|
|
|
@ -754,7 +754,7 @@ int up_addrenv_create(size_t textsize, size_t datasize,
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
int up_addrenv_destroy(group_addrenv_t addrenv);
|
||||
int up_addrenv_destroy(FAR group_addrenv_t *addrenv);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -776,7 +776,7 @@ int up_addrenv_destroy(group_addrenv_t addrenv);
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
int up_addrenv_vtext(FAR group_addrenv_t addrenv, FAR void **vtext);
|
||||
int up_addrenv_vtext(FAR group_addrenv_t *addrenv, FAR void **vtext);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -802,7 +802,7 @@ int up_addrenv_vtext(FAR group_addrenv_t addrenv, FAR void **vtext);
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
int up_addrenv_vdata(FAR group_addrenv_t addrenv, uintptr_t textsize,
|
||||
int up_addrenv_vdata(FAR group_addrenv_t *addrenv, uintptr_t textsize,
|
||||
FAR void **vdata);
|
||||
#endif
|
||||
|
||||
|
@ -832,7 +832,8 @@ int up_addrenv_vdata(FAR group_addrenv_t addrenv, uintptr_t textsize,
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv);
|
||||
int up_addrenv_select(FAR const group_addrenv_t *addrenv,
|
||||
FAR save_addrenv_t *oldenv);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -853,7 +854,7 @@ int up_addrenv_select(group_addrenv_t addrenv, save_addrenv_t *oldenv);
|
|||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
int up_addrenv_restore(save_addrenv_t oldenv);
|
||||
int up_addrenv_restore(FAR const save_addrenv_t *oldenv);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
#define MM_PGMASK (MM_PGSIZE - 1)
|
||||
#define MM_PGALIGNDOWN(a) ((uintptr_t)(a) & ~MM_PGMASK)
|
||||
#define MM_PGALIGNUP(a) (((uintptr_t)(a) + MM_PGMASK) & ~MM_PGMASK)
|
||||
#define MM_NPAGES(s) (((uintptr_t)(a) + MM_PGMASK) >> MM_PGSHIFT)
|
||||
#define MM_NPAGES(s) (((uintptr_t)(s) + MM_PGMASK) >> MM_PGSHIFT)
|
||||
#define MM_ISALIGNED(a) (((uintptr_t)(a) & MM_PGMASK) == 0)
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
@ -211,7 +211,7 @@ static inline void group_release(FAR struct task_group_s *group)
|
|||
#ifdef CONFIG_ARCH_ADDRENV
|
||||
/* Destroy the group address environment */
|
||||
|
||||
(void)up_addrenv_destroy(group->addrenv);
|
||||
(void)up_addrenv_destroy(&group->addrenv);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
|
|
Loading…
Reference in a new issue