091013

|


SH4A続き。前回は64MBページ8枚で512MBをマップした。今度はKB単位のページ
でマップしてみよう。

アドレス変換のエリアはU0,P0,P3の3エリアでそれぞれ
  U0 2GB
  P0 2GB
  P3 512MB
だ。なので2GBをマップできる用意をすればいい。
これをページサイズ4KBでマップすると2GB/4KBで524288エントリ必要だ。
それぞれのエントリを32bitで済ませば2MB。64bit使えば4MBがそのために必要。

2GBほとんどを使うアプリケーションでなければ、2GBのうちマップされるのは
数少なくなるため、昔からここはPTEエントリ自体も必要になった時にページを
確保するという戦略になっている。

ページサイズがnKBで、ここに登録されるエントリのサイズがmByteとすると、
一つのページをページテーブルにすると、PTE/PAGEはn * 1024 / m。それぞれの
PTEがnKBをマップするので、一つのページテーブルでn * 1024 * (n * 1024 / m)
をマップできることになる。

これで2GBをマップするには2 * 1024 * m / (n * n)のページテーブルページが
あればいい。

NetBSDではページサイズに4KB、PTEのサイズが4Byteなので、512個。2KBの領域
をスタティックにとっている。

SH4Aの場合、ページサイズを8KBにすればキャッシュエイリアス問題が生じなく
て都合がいい。そしてPTEにはPTELとPTEAの両方を設定できるといいので8Byte
がいいだろう。

ページサイズ8KB PTEサイズ8Bとなると、ページテーブルページのエントリは256。
そのエントリにはPTEが入るので、256*8で2KB必要。

いろいろな例。

これはNetBSD
/*
Page size: 4KB
Page table entry size: 4B
4MB / page table page.
512 page table page entry required. (total 2048byte)
page offset:
....................|||||||||||| 11 10 9 8 7 6 5 4 3 2 1 0 [0x00000fff] 4095
page table page offset. shift=12 mask=0x3ff
..........||||||||||............ 21 20 19 18 17 16 15 14 13 12 [0x003ff000] 4190208
page table page index.  shift=22, mask=0x1ff
.|||||||||...................... 30 29 28 27 26 25 24 23 22 [0x7fc00000] 2143289344
*/
#define	PAGE_SIZE		0x1000
#define	PTE_SIZE		4
#define	PTP_OFFSET		0x3ff000
#define	PTP_OFFSET_SHIFT	12
#define	PTP_OFFSET_MASK		0x3ff
#define	PTP_INDEX		0x7fc00000
#define	PTP_INDEX_SHIFT		22
#define	PTP_INDEX_MASK		0x1ff
#define	PTP_ENTRY		512

これはSH4Aに理想的じゃないかと思っているセッティング。
/*
Page size: 8KB
Page table entry size: 8B
8MB / page table page.
256 page table page entry required. (total 2048byte)
page offset:
...................||||||||||||| 12 11 10 9 8 7 6 5 4 3 2 1 0 [0x00001fff] 8191
page table page offset. shift=13 mask=0x3ff
.........||||||||||............. 22 21 20 19 18 17 16 15 14 13 [0x007fe000] 8380416
page table page index.  shift=23, mask=0xff
.||||||||....................... 30 29 28 27 26 25 24 23 [0x7f800000] 2139095040
*/
#define	PAGE_SIZE		0x2000
#define	PTE_SIZE		8
#define	PTP_OFFSET		0x7fe000
#define	PTP_OFFSET_SHIFT	13
#define	PTP_OFFSET_MASK		0x3ff
#define	PTP_INDEX		0x7f800000
#define	PTP_INDEX_SHIFT		23
#define	PTP_INDEX_MASK		0xff
#define	PTP_ENTRY		256

今日日このくらいの方がいいのでは?という挑戦的なセッティング。
/*
Page size: 64KB
Page table entry size: 8B
512MB / page table page.
4 page table page entry required. (total 32byte)
page offset:
................|||||||||||||||| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 [0x0000ffff] 65535
page table page offset. shift=16 mask=0x1fff
...|||||||||||||................ 28 27 26 25 24 23 22 21 20 19 18 17 16 [0x1fff0000] 536805376
page table page index.  shift=29, mask=0x3
.||............................. 30 29 [0x60000000] 1610612736
*/
#define	PAGE_SIZE		0x10000
#define	PTE_SIZE		8
#define	PTP_OFFSET		0x1fff0000
#define	PTP_OFFSET_SHIFT	16
#define	PTP_OFFSET_MASK		0x1fff
#define	PTP_INDEX		0x60000000
#define	PTP_INDEX_SHIFT		29
#define	PTP_INDEX_MASK		0x3
#define	PTP_ENTRY		4

この64KBページならむしろ、PTPはスタティックに64KB*4=256KBとってしまって
もいいかも。そしてページテーブルページが絶対メモリの中にあるならかなり
見通しがいい。

これらの#defineはSHIFTとMASKがどうしてもプリプロセッサでできないので、
これらの設定を吐き出すプログラムを先に作りました。

#include <sys/types.h>
#include <stdio.h>

#define	__BEGIN_MACRO	do {
#define	__END_MACRO	} while (/*CONSTCOND*/0)

#define	__bitdisp(a, s, e, m, c)					\
__BEGIN_MACRO								\
	u_int32_t __j, __j1;						\
	int __i, __s, __e, __n;						\
	__n = sizeof(typeof(a)) * NBBY - 1;				\
	__j1 = 1 << __n;						\
	__e = e ? e : __n;						\
	__s = s;							\
	for (__j = __j1, __i = __n; __j > 0; __j >>=1, __i--) {		\
		if (__i > __e || __i < __s) {				\
			printf("%c", a & __j ? '+' : '-');		\
		} else {						\
			printf("%c", a & __j ? '|' : '.');		\
		}							\
	}								\
	if (m) {							\
		printf("[%s]", (char*)m);				\
	}								\
	if (c) {							\
		for (__j = __j1, __i = __n; __j > 0; __j >>=1, __i--) {	\
			if (!(__i > __e || __i < __s) && (a & __j)) {	\
				printf(" %d", __i);			\
			}						\
		}							\
	}								\
	printf(" [0x%08x] %d", a, a);					\
	printf("\n");							\
__END_MACRO
#define	bitdisp(a) __bitdisp((a), 0, 0, 0, 1)

int
main (int argc, const char *argv[])
{
  if (argc < 3)
    return 0;

  uint32_t n = atoi (argv[1]);	// Page size (unit KB)
  uint32_t m = atoi (argv[2]);	// PTE size (unit B)

  printf ("/*\n");
  printf ("Page size: %dKB\nPage table entry size: %dB\n", n, m);
  uint32_t page_table_mapsize = n * n / m;
  uint32_t page_table_page_entry = 2 * 1024 * m / (n * n);
  printf ("%dMB / page table page.\n", page_table_mapsize);
  printf ("%d page table page entry required. (total %dbyte)\n",
	  page_table_page_entry, page_table_page_entry * m);

  printf ("page offset:\n");
  uint32_t PAGE_SIZE = n * 1024;
  uint32_t PTE_SIZE = m;
#define	PAGE_OFFSET	(PAGE_SIZE - 1)
// size of 1 page table page can map.
#define	PTP_MAPSIZE	(PAGE_SIZE / PTE_SIZE * PAGE_SIZE )
#define	PTP_OFFSET	((PTP_MAPSIZE - 1) & ~PAGE_OFFSET)
#define	PTP_INDEX	(~(PTP_MAPSIZE - 1) & ~0x80000000)
  bitdisp (PAGE_OFFSET);
  uint32_t ptp_offset_shift = ffs(PTP_OFFSET) - 1;
  printf ("page table page offset. shift=%d mask=0x%x\n", ptp_offset_shift,
	  PTP_OFFSET >> ptp_offset_shift);
  bitdisp (PTP_OFFSET);
  uint32_t ptp_shift = ffs(PTP_INDEX) - 1;
  printf ("page table page index.  shift=%d, mask=0x%x\n", ptp_shift,
	  PTP_INDEX >> ptp_shift);
  bitdisp (PTP_INDEX);
  printf ("*/\n");

  printf ("#define\tPAGE_SIZE\t\t0x%x\n", PAGE_SIZE);
  printf ("#define\tPTE_SIZE\t\t%d\n", PTE_SIZE);
  printf ("#define\tPTP_OFFSET\t\t0x%x\n", PTP_OFFSET);
  printf ("#define\tPTP_OFFSET_SHIFT\t%d\n", ptp_offset_shift);
  printf ("#define\tPTP_OFFSET_MASK\t\t0x%x\n", PTP_OFFSET >> ptp_offset_shift);
  printf ("#define\tPTP_INDEX\t\t0x%x\n", PTP_INDEX);
  printf ("#define\tPTP_INDEX_SHIFT\t\t%d\n", ptp_shift);
  printf ("#define\tPTP_INDEX_MASK\t\t0x%x\n", PTP_INDEX >> ptp_shift);
  printf ("#define\tPTP_ENTRY\t\t%d\n", page_table_page_entry);

  return 0;
}