mirror of https://github.com/l4ka/hazelnut.git
507 lines
12 KiB
C
507 lines
12 KiB
C
/*********************************************************************
|
|
*
|
|
* Copyright (C) 2001-2002, Karlsruhe University
|
|
*
|
|
* File path: sigma0/sigma0-new.c
|
|
* Description: Implementation of new generic sigma0 protocol.
|
|
*
|
|
* @LICENSE@
|
|
*
|
|
* $Id: sigma0-new.c,v 1.12 2002/06/07 17:36:30 skoglund Exp $
|
|
*
|
|
********************************************************************/
|
|
#include <l4/l4.h>
|
|
#include <l4/arch/kernel.h>
|
|
#include <l4/sigma0.h>
|
|
#include <l4io.h>
|
|
|
|
#if 1
|
|
extern "C" void *memset(void *b, int c, int size)
|
|
{
|
|
char *p = (char *) b;
|
|
|
|
while ( size-- )
|
|
*(p++) = c;
|
|
|
|
return b;
|
|
}
|
|
#endif
|
|
|
|
|
|
#define iskernelthread(x) (x.raw < myself.raw)
|
|
#define NO_MAP (~0UL)
|
|
|
|
typedef byte_t space_id_t;
|
|
|
|
l4_kernel_info_t *kip;
|
|
|
|
|
|
#if defined(CONFIG_ARCH_ARM)
|
|
|
|
/*
|
|
* ARM configuration.
|
|
*/
|
|
|
|
#define DEFAULT_SIZE (12)
|
|
#define PAGE_BITS (12)
|
|
#define PAGE_SIZE (1 << PAGE_BITS)
|
|
#define MAX_MEM (2*256L*1024L*1024L)
|
|
#define PAGE_SIZES (3)
|
|
|
|
space_id_t page_array[MAX_MEM >> PAGE_BITS];
|
|
dword_t page_shifts[PAGE_SIZES+1] = { 12, 16, 20, 32 };
|
|
dword_t page_sizes[PAGE_SIZES] = { (1<<12), (1<<16), (1<<20) };
|
|
|
|
#elif defined(CONFIG_ARCH_X86)
|
|
|
|
/*
|
|
* X86 configuration.
|
|
*/
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
|
|
#define IOFP_LO_BOUND 0xF0000000
|
|
#define IOFP_HI_BOUND 0xFFFFF000
|
|
|
|
#define PORT_FREE 0
|
|
|
|
#define IOFP_PORT(x) ((dword_t) (((x) >> 12) & 0x0000FFFF))
|
|
#define IOFP_LD_SIZE(x) ((dword_t) (((x) >> 2) & 0x0000003F))
|
|
#define IOFP_SIZE(x) ((dword_t) (1 << IOFP_LD_SIZE(x)))
|
|
#define MIN(a,b) (a<b?a:b)
|
|
|
|
#define X86_IO_SPACE_SIZE (1<<16)
|
|
static space_id_t io_space[X86_IO_SPACE_SIZE];
|
|
dword_t allocate_io_space(space_id_t space, dword_t addr);
|
|
void free_io_space(space_id_t space, dword_t addr);
|
|
#endif /* CONFIG_IO_FLEXPAGES */
|
|
|
|
#define DEFAULT_SIZE (12)
|
|
#define PAGE_BITS (12)
|
|
#define PAGE_SIZE (1 << PAGE_BITS)
|
|
#define MAX_MEM (256L*1024L*1024L)
|
|
#define PAGE_SIZES (2)
|
|
|
|
space_id_t page_array[MAX_MEM >> PAGE_BITS];
|
|
dword_t page_shifts[PAGE_SIZES+1] = { 12, 22, 32 };
|
|
dword_t page_sizes[PAGE_SIZES] = { (1<<12), (1<<22) };
|
|
|
|
#else
|
|
#error Page size specifications are lacking for this architecture.
|
|
#endif
|
|
|
|
|
|
/*
|
|
**
|
|
** Sigma0 protocol.
|
|
**
|
|
*/
|
|
|
|
void init_space(l4_kernel_info_t *kip);
|
|
void free_space(space_id_t space, dword_t addr, dword_t size);
|
|
dword_t allocate_space(space_id_t space, dword_t addr, dword_t size);
|
|
dword_t allocate_space(space_id_t space, dword_t size);
|
|
|
|
|
|
extern "C" void sigma0_main(l4_kernel_info_t *_kip)
|
|
{
|
|
l4_threadid_t client;
|
|
l4_msgdope_t result;
|
|
l4_fpage_t fp;
|
|
dword_t dw0, dw1;
|
|
dword_t msg, sz;
|
|
int r;
|
|
|
|
kip = _kip;
|
|
|
|
l4_threadid_t myself = l4_myself();
|
|
|
|
printf("sigma0: kernel_info_page is at %p\n", kip);
|
|
|
|
if ( (((char *) kip)[0] != 'L') | (((char *) kip)[1] != '4') |
|
|
(((char *) kip)[2] != 0xE6) | (((char *) kip)[3] != 'K') )
|
|
enter_kdebug("sigma0: invalid KIP");
|
|
|
|
init_space(kip);
|
|
|
|
for (;;)
|
|
{
|
|
r = l4_ipc_wait(&client, NULL,
|
|
&dw0, &dw1, &fp.raw,
|
|
L4_IPC_NEVER, &result);
|
|
|
|
for (;;)
|
|
{
|
|
// printf("sigma0: tid=%08x dw0=%08x dw1=%08x dw2=%08x (rcvd)\n",
|
|
// client.raw, dw0, dw1, fp.raw);
|
|
|
|
/*
|
|
* Handle compatibility with pagefault protocol and older
|
|
* versions of sigma0 RPC.
|
|
*/
|
|
|
|
if ( dw0 == 0xfffffffc )
|
|
/* Map arbitrary 4K page (old s0 compitability). */
|
|
fp = l4_fpage(0, 12, 0, 1);
|
|
else if ( (fp.raw == 0 && dw0 != 0x1) || /* (PF compitability) */
|
|
((dw0 & 0x3) == 0) ) /* (old s0 compitability) */
|
|
{
|
|
/* Map indicated page frame. */
|
|
#if defined(CONFIG_ARCH_X86)
|
|
if ( dw0 > (1024*1024*1024) )
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
if (dw0 >= IOFP_LO_BOUND && dw0 <= IOFP_HI_BOUND)
|
|
fp.raw = dw0 + 0x3;
|
|
else
|
|
#endif
|
|
fp = l4_fpage(dw0 + (1024*1024*1024), 22, 1, 1);
|
|
else
|
|
#endif
|
|
fp = l4_fpage(dw0, DEFAULT_SIZE, 1, 1);
|
|
}
|
|
#if defined(CONFIG_ARCH_X86)
|
|
else if ( ((dw0 & 0x3) == 0x1) && (dw1 == (22 << 2)) )
|
|
/* Map indicated page frame (old s0 compitability). */
|
|
fp = l4_fpage(dw0, 22, 1, 1);
|
|
#endif
|
|
|
|
if ( dw0 != 0x1 )
|
|
dw0 = 0x3;
|
|
|
|
// printf("sigma0: tid=%08x dw0=%08x dw1=%08x dw2=%08x (ok)\n",
|
|
// client.raw, dw0, dw1, fp.raw);
|
|
|
|
|
|
msg = 2; /* We usually do a snd_fpage IPC. */
|
|
sz = fp.fp.size;
|
|
|
|
|
|
if ( dw0 == 0x1 )
|
|
{
|
|
if ( iskernelthread(client) )
|
|
/* Recomended amount of in-kernel data. */
|
|
msg = 0, dw0 = 0x100 << PAGE_BITS;
|
|
else
|
|
{
|
|
/* Kernel-info-page request. */
|
|
dw0 = (dword_t) kip;
|
|
fp = l4_fpage(dw0, 12, 0, 0);
|
|
}
|
|
}
|
|
else if ( fp.raw & 0x1 )
|
|
{
|
|
if ( fp.raw & 0x2 )
|
|
/* Map the specified fpage. */
|
|
dw0 = allocate_space(client.id.task, fp.raw, sz);
|
|
else
|
|
/* Map fpage from an arbitrary location. */
|
|
dw0 = allocate_space(client.id.task, sz);
|
|
|
|
if ( dw0 != NO_MAP )
|
|
{
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
if (fp.raw >= IOFP_LO_BOUND && fp.raw <= IOFP_HI_BOUND)
|
|
fp.raw = dw0 = dw0 & ~0x3;
|
|
else
|
|
#endif
|
|
{
|
|
l4_fpage_t old_fp = fp;
|
|
fp = l4_fpage(dw0, sz, 1, iskernelthread(client));
|
|
if (! iskernelthread(client))
|
|
{
|
|
fp.fp.uncacheable = old_fp.fp.uncacheable;
|
|
fp.fp.unbufferable = old_fp.fp.unbufferable;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
msg = dw0 = fp.raw = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Free up space. Do not send reply. */
|
|
free_space(client.id.task, fp.raw, sz);
|
|
break;
|
|
}
|
|
|
|
// printf("sigma0: reply msg=%08x dw0=%08x dw1=%08x\n",
|
|
// msg, dw0, fp.raw);
|
|
|
|
r = l4_ipc_reply_and_wait(client, (void *) msg,
|
|
dw0, fp.raw, 0,
|
|
&client, NULL,
|
|
&dw0, &dw1, &fp.raw,
|
|
L4_IPC_NEVER, &result);
|
|
|
|
if ( L4_IPC_ERROR(result) )
|
|
{
|
|
printf("sigma0: error reply_and_wait (%x)\n", result.raw);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
**
|
|
** Space management.
|
|
**
|
|
*/
|
|
|
|
#define PAGE_RESERVED 0xFF
|
|
#define PAGE_FREE 0xFE
|
|
#define PAGE_SHARED 0xFC
|
|
#define PAGE_ROOT 0x04
|
|
|
|
#define PRINT_KIP
|
|
|
|
#if defined(PRINT_KIP)
|
|
#define printkip(name, attrib) \
|
|
printf(" kip: %s \t<%08x,%08x> (%s)\n", #name, \
|
|
kip->##name##.low, kip->##name##.high, #attrib)
|
|
#else
|
|
#define printkip(name, attrib)
|
|
#endif
|
|
|
|
void init_space(l4_kernel_info_t *kip)
|
|
{
|
|
dword_t i;
|
|
|
|
for ( i = 0; i < (MAX_MEM >> PAGE_BITS); i++ )
|
|
page_array[i] = PAGE_RESERVED;
|
|
|
|
#define setmem(name, attrib) \
|
|
if ( kip->##name##.high ) \
|
|
{ \
|
|
printkip(name, attrib); \
|
|
if ( (kip->##name##.low >= kip->main_memory.low) && \
|
|
(kip->##name##.high <= kip->main_memory.high) ) \
|
|
{ \
|
|
for ( i = kip->##name##.low >> PAGE_BITS; \
|
|
i < (kip->##name##.high) >> PAGE_BITS; \
|
|
i++ ) \
|
|
\
|
|
page_array[i - (kip->main_memory.low >> PAGE_BITS)] = \
|
|
PAGE_##attrib; \
|
|
} \
|
|
else \
|
|
printf("kip->" #name " is wrong\n"); \
|
|
}
|
|
|
|
setmem(main_memory, FREE);
|
|
setmem(dedicated[0], SHARED);
|
|
setmem(dedicated[1], SHARED);
|
|
setmem(dedicated[2], SHARED);
|
|
setmem(dedicated[3], SHARED);
|
|
setmem(dedicated[4], SHARED);
|
|
|
|
setmem(reserved0, RESERVED);
|
|
setmem(reserved1, RESERVED);
|
|
setmem(root_memory, ROOT);
|
|
setmem(sigma0_memory, RESERVED);
|
|
setmem(sigma1_memory, RESERVED);
|
|
setmem(kdebug_memory, RESERVED);
|
|
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
for (dword_t idx = 0; idx < X86_IO_SPACE_SIZE; idx++)
|
|
io_space[idx] = PORT_FREE;
|
|
#endif
|
|
}
|
|
|
|
|
|
dword_t allocate_space(space_id_t space, dword_t addr, dword_t size)
|
|
{
|
|
if (addr < kip->main_memory.low)
|
|
{
|
|
printf("s0: %x < %x", addr, kip->main_memory.low);
|
|
return NO_MAP;
|
|
}
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
if (addr >= IOFP_LO_BOUND && addr <= IOFP_HI_BOUND)
|
|
return allocate_io_space(space, addr);
|
|
|
|
#endif
|
|
if ( size < PAGE_BITS )
|
|
return NO_MAP;
|
|
|
|
addr &= ~((1 << size)-1);
|
|
|
|
/* Check if pages can be allocated. */
|
|
dword_t tmp = addr - kip->main_memory.low;
|
|
dword_t num = 1 << (size - PAGE_BITS);
|
|
dword_t idx = tmp >> PAGE_BITS;
|
|
|
|
for ( ; tmp < MAX_MEM && num--; idx++, tmp += PAGE_SIZE )
|
|
{
|
|
if ( ! ((page_array[idx] == PAGE_FREE) ||
|
|
(page_array[idx] == PAGE_SHARED) ||
|
|
(page_array[idx] == space)) )
|
|
return NO_MAP;
|
|
}
|
|
|
|
if ( tmp >= MAX_MEM )
|
|
return NO_MAP;
|
|
|
|
/* Mark pages as allocated. */
|
|
tmp = addr - kip->main_memory.low;
|
|
num = 1 << (size - PAGE_BITS);
|
|
idx = tmp >> PAGE_BITS;
|
|
|
|
for ( ; num--; idx++ )
|
|
if ( page_array[idx] == PAGE_FREE )
|
|
page_array[idx] = space;
|
|
|
|
return addr;
|
|
}
|
|
|
|
|
|
dword_t allocate_space(space_id_t space, dword_t size)
|
|
{
|
|
dword_t addr, tmp, idx, num;
|
|
|
|
if ( size < PAGE_BITS )
|
|
return NO_MAP;
|
|
|
|
/* Search for free location. */
|
|
for ( addr = 0; addr < MAX_MEM; addr += (1 << size) )
|
|
{
|
|
tmp = addr;
|
|
num = (1 << (size - PAGE_BITS)) + 1;
|
|
idx = tmp >> PAGE_BITS;
|
|
|
|
for ( ; tmp <= MAX_MEM && --num; idx++, tmp += PAGE_SIZE )
|
|
if ( page_array[idx] != PAGE_FREE )
|
|
break;
|
|
|
|
if ( num == 0 )
|
|
{
|
|
/* Mark pages as allocated. */
|
|
tmp = addr;
|
|
num = 1 << (size - PAGE_BITS);
|
|
idx = tmp >> PAGE_BITS;
|
|
|
|
for ( ; num--; idx++ )
|
|
if ( page_array[idx] == PAGE_FREE )
|
|
page_array[idx] = space;
|
|
|
|
return kip->main_memory.low + addr;
|
|
}
|
|
else if ( tmp >= MAX_MEM )
|
|
return NO_MAP;
|
|
}
|
|
|
|
return NO_MAP;
|
|
}
|
|
|
|
|
|
void free_space(space_id_t space, dword_t addr, dword_t size)
|
|
{
|
|
dword_t snum, plus, tmp, num, idx, sz, endaddr;
|
|
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
if (addr >= IOFP_LO_BOUND && addr <= IOFP_HI_BOUND){
|
|
free_io_space(space, addr);
|
|
return;
|
|
}
|
|
#endif
|
|
if ( size < PAGE_BITS )
|
|
return;
|
|
|
|
addr &= ~((1 << size)-1);
|
|
endaddr = addr + (1 << size) - PAGE_SIZE;
|
|
|
|
/*
|
|
* Freeing pages is tricky because we can not free a smaller page
|
|
* if we have mapped a larger page to the task. E.g., we can not
|
|
* free a 4KB page residing within an already mapped 4MB page.
|
|
*/
|
|
for ( sz = PAGE_SIZES; sz-- > 0; )
|
|
{
|
|
snum = size > page_shifts[sz] ? (1 << (size - page_shifts[sz])) : 1;
|
|
plus = 0;
|
|
|
|
for ( ; snum--; plus += page_sizes[sz] )
|
|
{
|
|
tmp = (addr & ~(page_sizes[sz]-1)) + plus;
|
|
num = 1 << (page_shifts[sz] - PAGE_BITS);
|
|
idx = tmp >> PAGE_BITS;
|
|
|
|
/*
|
|
* Check if task owns all pages within a larger page for
|
|
* this region. E.g., for x86 we need to check if a task
|
|
* owns all 4KB pages within a 4MB region.
|
|
*/
|
|
while ( (page_array[idx] == space) && --num ) idx++;
|
|
|
|
if ( num == 0 )
|
|
{
|
|
/*
|
|
* Task does indeed own all pages within the larger
|
|
* region. We unmap the whole region to make sure
|
|
* that the page is properly unmapped.
|
|
*/
|
|
l4_fpage_unmap(l4_fpage(tmp, page_shifts[sz], 0, 0),
|
|
(L4_FP_OTHER_SPACES|L4_FP_FLUSH_PAGE));
|
|
|
|
/*
|
|
* We now free up the actual unallocated pages. It
|
|
* might be that we have flushed too many pages above,
|
|
* but this should be handled afterwards by the
|
|
* underlying application (i.e., its pager).
|
|
*/
|
|
num = 1 << (page_shifts[sz] - PAGE_BITS);
|
|
idx = tmp >> PAGE_BITS;
|
|
for ( ; num--; idx++, tmp += PAGE_SIZE )
|
|
if ( tmp >= addr && tmp <= endaddr &&
|
|
page_array[idx] == space )
|
|
page_array[idx] = PAGE_FREE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if defined(CONFIG_IO_FLEXPAGES)
|
|
|
|
dword_t allocate_io_space(space_id_t space, dword_t addr){
|
|
|
|
dword_t size = IOFP_SIZE(addr);
|
|
dword_t port = IOFP_PORT(addr);
|
|
|
|
|
|
if (port + size > X86_IO_SPACE_SIZE)
|
|
return NO_MAP;
|
|
|
|
for (dword_t idx = port; idx < port + size; idx++){
|
|
if (io_space[idx] != PORT_FREE && io_space[idx] != space)
|
|
return NO_MAP;
|
|
}
|
|
|
|
for (dword_t idx = port; idx < port + size; idx++){
|
|
io_space[idx] = space;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
void free_io_space(space_id_t space, dword_t addr){
|
|
|
|
dword_t size = IOFP_SIZE(addr);
|
|
dword_t port = IOFP_PORT(addr);
|
|
|
|
if (port + size > X86_IO_SPACE_SIZE)
|
|
return;
|
|
|
|
for (dword_t idx = port; idx < port + size; idx++){
|
|
if (io_space[idx] != space && io_space[idx] != PORT_FREE)
|
|
return;
|
|
}
|
|
|
|
for (dword_t idx = port; idx < port + size; idx++){
|
|
io_space[idx] = PORT_FREE;
|
|
}
|
|
|
|
printf("sigma0: unmap IO-Page port = %x, size=%x, fp=%x\n", port, size, addr);
|
|
l4_fpage_unmap( ( (l4_fpage_t){raw: addr}), 0);
|
|
|
|
}
|
|
#endif /* CONFIG_IO_FLEXPAGES */
|