メッセージ欄

2013年11月の日記

一覧で表示する

rpi_start.Sを読む (第4回)
2013/11/05(火) 27:06 NetBSD はてブ情報 はてブに登録 はてブ数

続きです、と行きたいところですが、今回は先に以下の部分を読み解きます。
    308 mmu_init_table:
    309 	/* Add 1MB of VA==PA at 0x00000000 so we can keep the kernel going */
    310 	MMU_INIT(0x0, 0x0,
    311 	    (_end - KERNEL_BASE + 2 * L1_S_SIZE - 1),
    312 	    L1_S_PROTO | L1_S_AP_KRW)
    313 
    314 	MMU_INIT(KERNEL_BASE, 0x0,
    315 	    (_end - KERNEL_BASE + 2 * L1_S_SIZE - 1),
    316 	    L1_S_PROTO | L1_S_AP_KRW  | L1_S_B | L1_S_C)
    317 
    318 	/* Map the 16MB of peripherals */
    319 	MMU_INIT(RPI_KERNEL_IO_VBASE, RPI_KERNEL_IO_PBASE,
    320 	    (RPI_KERNEL_IO_VSIZE + L1_S_SIZE - 1),
    321 	    L1_S_PROTO | L1_S_AP_KRW)
    322 
    323 	/* end of table */
    324 	MMU_INIT(0, 0, 0, 0)
MMU_INITとはすぐ上にあるマクロのようです。
    301 /* We'll modify va and pa at run time so we can use relocatable addresses. */
    302 #define MMU_INIT(va,pa,n_sec,attr) \
    303 	.word	va					    ; \
    304 	.word	pa					    ; \
    305 	.word	n_sec					    ; \
    306 	.word	attr					    ;
単純に引数を.wordで並べればいいということですね。301行目のコメントが気になりますよねぇ。訳すと、再配置可能なアドレスを使えるように、実行時にva(仮想アドレス)とpa(物理アドレス)を変換する、といったところでしょうか。引数はそれぞれ、仮想アドレスと物理アドレスの先頭、n_sec はセクション数ですかね。attrはそのアドレス領域の属性でしょう。

では、一行目のMMU_INITからいってみましょう。
    309 	/* Add 1MB of VA==PA at 0x00000000 so we can keep the kernel going */
    310 	MMU_INIT(0x0, 0x0,
    311 	    (_end - KERNEL_BASE + 2 * L1_S_SIZE - 1),
    312 	    L1_S_PROTO | L1_S_AP_KRW)
MMU_INITの引数を読み解いていきます。最初の二つ0x0, 0x0はいいですね。va=0x0, pa=0x0です。

三つめは(_end - KERNEL_BASE + 2 * L1_S_SIZE - 1)です。これは面倒ですね。_end, KERNEL_BASE, L1_S_SIZEはどう定義されているのでしょう。

まず、KERNEL_BASEはarch/evbarm/include/vmparam.hに定義されています。
  #ifdef KERNEL_BASE_EXT
  #define    KERNEL_BASE        KERNEL_BASE_EXT
  #else
  #define    KERNEL_BASE        0x80000000
  #endif
おっと、KERNEL_BASE_EXTが必要になりました。
KERNEL_BASE_EXTはconf/std.rpiに定義されています。
  options         KERNEL_BASE_EXT=0xc0000000
次に、_endですが、うーん、arch/evbarm/conf/ldscript.evbarmになるのかな。
    }
    . = ALIGN(32 / 8);
    _end = .;
    _bss_end__ = . ; __bss_end__ = . ; __end__ = . ;
    PROVIDE (end = .);
  }
_endの具体的な定義はどうなるのでしょうか? カーネルをreadelfしてendというシンボルを探すと(ABSとendで探す) ちゃんとあるようです。

NetBSD 6.1_RC1のnetbsd-RPI.gzをreadelfしてみると、次のようになります。
  n61rc1# readelf -a netbsd-RPI|grep end|grep ABS
  
   6634: c0443034     0 NOTYPE  GLOBAL DEFAULT  ABS end
  10028: c0443034     0 NOTYPE  GLOBAL DEFAULT  ABS __end__
  10100: c0443034     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__
  11198: c0443034     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__
  12138: c0443034     0 NOTYPE  GLOBAL DEFAULT  ABS _end
ですから_end = 0xc0443034ということになりそうです。

残りのL1_S_SIZEは先ほども出てきたpte.hに書かれています。
  #define    L1_S_SIZE    0x00100000    /* 1M */
  #define    L1_S_OFFSET    (L1_S_SIZE - 1)
  #define    L1_S_FRAME    (~L1_S_OFFSET)
  #define    L1_S_SHIFT    20
これで謎だった_end, KERNEL_BASE, L1_S_SIZEがわかりました。

n_sec = (_end - KERNEL_BASE + 2 * L1_S_SIZE - 1)は、_end - KERNEL_BASEはkernelの末尾 - kernelの頭、つまりkernelのサイズと読めます。それにL1_S_SIZEつまり1MBの二倍、2MBを足して1を引くだから、0x1FFFFFを足すと以下のように計算されます。
  n_sec = 0xc0443034 - 0xC0000000 + 2 * 0x00100000 - 1 
        = 0x643033;
だいぶ長くなりました。どこを読んでいたんでしたっけ? ということで再掲します。
    309 	/* Add 1MB of VA==PA at 0x00000000 so we can keep the kernel going */
    310 	MMU_INIT(0x0, 0x0,
    311 	    (_end - KERNEL_BASE + 2 * L1_S_SIZE - 1),
    312 	    L1_S_PROTO | L1_S_AP_KRW)
MMU_INITの最後の引数、attr = L1_S_PROTO | L1_S_AP_KRWを読みましょう。

ここで、attrつまりアトリビュートがどのようなものなのかイメージするために、ページテーブルの形式について触れておきます。

MMUはメモリアクセスの方式を複数選択でき、ページ(タイニー、スモール、ラージの三種)かセクション(セクション、スーパーセクション?)を選びます。NetBSD/rpiではセクションタイプのページテーブルを使っています。

セクションタイプのページテーブルは、ベースアドレス12ビットとアトリビュート20ビットから構成されています。
31-20191817161514-1211-1098-543210
セクションのベースアドレス(12bit)SBZ0nGSAPXTEXAPIMPドメインXNCB10
セクションの単位は1MBです。32bitメモリアドレスのCPUで表現できるアドレス空間は4GBですから、それを1MB単位で管理するには4096個のページテーブルエントリが必要になります。
4096は0も含めると0-4095で表現できますから4095=0xFFFつまり12ビットあればよいわけです。

あらためてattr = L1_S_PROTO | L1_S_AP_KRWを読みましょう。

L1_S_PROTO のSはどうやらセクションのことのようです。その頭文字をとってSなのでしょう。

L1_S_PROTOを追うと、pmap.hの L1_TYPE_Sに行き着きました。
  #define L1_S_PROTO_generic      (L1_TYPE_S | L1_S_IMP)
L1_TYPE_S, L1_S_IMPはpte.hで定義されています。
  /*
   * ARM L1 Descriptors
   */
  #define L1_TYPE_INV     0x00            /* Invalid (fault) */
  #define L1_TYPE_C       0x01            /* Coarse L2 */
  #define L1_TYPE_S       0x02            /* Section */
  #define L1_TYPE_F       0x03            /* Fine L2 */
  #define L1_TYPE_MASK    0x03            /* mask of type bits */
  
  /* L1 Section Descriptor */
  #define L1_S_B          0x00000004      /* bufferable Section */
  #define L1_S_C          0x00000008      /* cacheable Section */
  #define L1_S_IMP        0x00000010      /* implementation defined */
  #define L1_S_DOM(x)     ((x) << 5)      /* domain */
  #define L1_S_DOM_MASK   L1_S_DOM(0xf)
  #define L1_S_AP(x)      ((x) << 10)     /* access permissions */
  #define L1_S_ADDR_MASK  0xfff00000      /* phys address of section */
L1_TYPE_Sは0x02で定義されており、テーブルの下位2bitを表現しています。
さきのページテーブルの表でも下位2bitは10でしたよね。
L1_S_IMPは0X00000010で定義されています。IMPがimplementationの略だというのはわかりますが、なにをimplementatiionしているんでしょうかねぇ。

ということで、L1_S_PROTO = L1_S_PROTO_generic = L1_TYPE_S | L1_S_IMP = 0x02 | 0x00000010 = 0x0012になります。

つぎは、L1_S_AP_KRWです。

L1_S_AP_KRWはgenassym.cfでマクロになっていて、
  define  L1_S_AP_KRW             L1_S_AP(AP_KRW)
からの
  #define       L1_S_AP(x)      ((x) << 10)     /* access permissions */
なのですね。APはAccess Permissionsの略称だったのですね。

L1_S_AP(AP_KRW)のAP_KRWもおなじみpte.hで定義されています。
  /*
   * Short-hand for common AP_* constants.
   *
   * Note: These values assume the S (System) bit is set and
   * the R (ROM) bit is clear in CP15 register 1.
   */
  #define AP_KR           0x00            /* kernel read */
  #define AP_KRW          0x01            /* kernel read/write */
  #define AP_KRWUR        0x02            /* kernel read/write usr read */
  #define AP_KRWURW       0x03            /* kernel read/write usr read/write */
名前の謎がだいぶ解けてきました。SがSystem bit、KRWがkernel read/writeということのようです。

まとめると、L1_S_AP_KRW = 0x01 << 10 = 0x0400になります。

L1_S_PROTO | L1_S_AP_KRW = 0x0012 | 0x0400 = 0x0412ということがわかりました。

MMU_INITの引数がすべてわかりました。
    309 	/* Add 1MB of VA==PA at 0x00000000 so we can keep the kernel going */
    310 	MMU_INIT(0x0, 0x0,
    311 	    (_end - KERNEL_BASE + 2 * L1_S_SIZE - 1),
    312 	    L1_S_PROTO | L1_S_AP_KRW)
というのは次のように表現できます。
  va = 0x0
  pa = 0x0;
  n_sec = 0xc0443034 - 0xC0000000 + 2 * 0x00100000 - 1 
        = 0x643033;
  attr =  0x0412

rpi_start.Sを読む (第3回)
2013/11/04(月) 22:16 NetBSD はてブ情報 はてブに登録 はてブ数

続きです。

まずは、コメントです。
    132 	/*
    133 	 * Set up a preliminary mapping in the MMU to allow us to run
    134 	 * at KERNEL_BASE with caches on.
    135 	 */
キャッシュをONにしてKERNEL_BASEで動かすため、MMUに準備的なマッピングをセットアップします、とでも訳すのでしょうか? なんのこっちゃ。
MMUに最低限の準備をします、ぐらいにとらえておけばよいのでしょうかねぇ。

さて、今回のメインはこちらです。
    136 	/* Build page table from scratch */
    137 	ldr	r0, Ltemp_l1_table	/* The page table address - entered into TTB later */
    138 	mov	r1, r0			/* Start address to clear memory. */
    139 	/* Zero the entire table so all virtual addresses are invalid. */
    140 	mov	r2, #L1_TABLE_SIZE	/* in bytes */
    141 	mov	r3, #0
    142 	mov	r4, r3
    143 	mov	r5, r3
    144 	mov	r6, r3
    145 	mov	r7, r3
    146 	mov	r8, r3
    147 	mov	r10, r3
    148 	mov	r11, r3
    149 1:	stmia	r1!, {r3-r8,r10-r11}
    150 	stmia	r1!, {r3-r8,r10-r11}
    151 	stmia	r1!, {r3-r8,r10-r11}
    152 	stmia	r1!, {r3-r8,r10-r11}
    153 	subs	r2, r2, #(4 * 4 * 8)	/* bytes per loop */
    154 	bne	1b
ちょっと長いですが、似た命令が多いので、大丈夫でしょう。
    136 	/* Build page table from scratch */
ページテーブルをスクラッチからつくりますというコメントです。なるほど、以降の処理はページテーブルに関する処理というわけですね。

先に進みます。
    137 	ldr	r0, Ltemp_l1_table	/* The page table address - entered into TTB later */
Ltemp_l1_tableのアドレスをr0にロードします。TTBとはTransration Table Baseの略ですね。Ltemp_l1_tableのところは、
    258 Ltemp_l1_table:
    259 	/* Put the temporary L1 translation table just below the kernel. */
    260 	.word	0x4000
と書かれています。kernelのすぐ後ろにテンポラリのL1変換テーブルを置く、と読めます。0x4000の大きさのテーブルが確保されているってことですかね。

まぁ、とにかくL1変換テーブルの先頭をr0に代入したという感じです。

次に進みます。
    138 	mov	r1, r0			/* Start address to clear memory. */
これは、先ほどのr0つまりL1変換テーブルの先頭をr1にも代入しています。
あとでr0を使うので温存しておいて、r1のほうを作業用に使うという感じですかね。

次に進みます。
    139 	/* Zero the entire table so all virtual addresses are invalid. */
テーブル全体をゼロにして、すべての仮想アドレスをinvalidにします。というコメントです。なんだかよくわかりませんが、ゼロにするようです。
    140 	mov	r2, #L1_TABLE_SIZE	/* in bytes */
L1_TABLE_SIZEはarch/arm/include/arm32/pte.hで
  #define    L1_TABLE_SIZE    0x4000        /* 16K */
と定義されていますので、
  r2 = 0x4000; /* L1_TABLE_SIZE */
ということになりますね。そういえば、さっきのL1_temp_tableは
    260 	.word	0x4000
でしたね。なるほど、関係ありそうです。

先に進みましょう。
    141 	mov	r3, #0
    142 	mov	r4, r3
    143 	mov	r5, r3
    144 	mov	r6, r3
    145 	mov	r7, r3
    146 	mov	r8, r3
    147 	mov	r10, r3
    148 	mov	r11, r3
レジスタr3に0を代入し、それをレジスタr4, r5, r6, r7, r8, r10, r11に代入しています。つまり、8つのレジスタをぜんぶゼロにしているようです。

ここから、ループに入ります。
    149 1:	stmia	r1!, {r3-r8,r10-r11}
    150 	stmia	r1!, {r3-r8,r10-r11}
    151 	stmia	r1!, {r3-r8,r10-r11}
    152 	stmia	r1!, {r3-r8,r10-r11}
stmiaです。stmはレジスタ-メモリ転送命令の複数版で、iaはIncrement Afterということで、メモリアクセス後にポインタを増やす、でしたね。

ということで、r1つまりL1_temp_tableの先頭からゼロを代入した8つのレジスタをどんどんメモリに転送していきます。

次に進みます。
    153 	subs	r2, r2, #(4 * 4 * 8)	/* bytes per loop */
subsのsubは減算命令です。subsの後ろのsはステータスレジスタを更新するためののsです。

L1_TABLE_SIZE(つまり0x4000)が格納されたr2から4*4*8を引きます。レジスタは32ビットなので4, stmiaが4回なので4, レジスタは8個転送されているので4*4*8ですね。

次に進みます。
    154 	bne	1b
bneのbは分岐命令です。先のsubsの末尾のsで更新されたステータスフラグをみて、分岐します。見るステータスフラグはZ、つまりゼロかどうかです。

bneのneはNot Equalつまりbneはr2がゼロでなければ1bに飛べと言っています。1bとは149行目の1:というラベルです。1bのbはbackのbでしょうか?

今回の処理をCっぽく書いてみると次のような感じでしょうか。
  r0 = &(Ltemp_l1_table);
  r1 = r0;
  r2 = 0x4000; /* L1_TABLE_SIZE */
  r3 = 0;
  r4 = r3;
  r5 = r3;
  r6 = r3;
  r7 = r3;
  r8 = r3;
  r10 = r3;
  r11 = r3;
  
  do {
    for (int i = 0; i < 4; i++) {
      *(r1++) = r3;
      *(r1++) = r4;
      *(r1++) = r5;
      *(r1++) = r6;
      *(r1++) = r7;
      *(r1++) = r8;
      *(r1++) = r10;
      *(r1++) = r11;
    }
    
    r2 -= (4 * 4 * 8);
  
  } while (r2 != 0)
素朴に、r3に0をセットして0x1000回のループにすればよいと思ったのですが、stmiaで8レジスタ一気に転送して、0x4000/(4*4*8)回のループのほうが効率的なんでしょうね。

さてこれで、Ltemp_l1_tableが全部ゼロクリアされた状態になりました。

rpi_start.Sを読む (第2回)
2013/11/04(月) 21:21 NetBSD はてブ情報 はてブに登録 はてブ数

つづきです。

今回読むのはここです。
    127 	mrs	r0, cpsr
    128 	bic	r0, r0, #PSR_MODE
    129 	orr	r0, r0, #(I32_bit | F32_bit | PSR_SVC32_MODE)
    130 	msr	cpsr, r0
まず127行目。
    127 	mrs	r0, cpsr
mrsはステータスレジスタのシステムコールです。mrsはロード、msrはストア。
read status, store registerの頭文字でしょうか。

cpsrは現在のステータスレジスタです(Current Program Status Register)。

現在のステータスレジスタをr0にロードする、ということですね。

次に進みます。
    128 	bic	r0, r0, #PSR_MODE
bicはビットクリアです。#PSR_MODEはarm/include/armreg.hで定義されていて下位5ビットに1が立っています。
  #define PSR_MODE    0x0000001f    /* mode mask */
ということで、r0(ステータスレジスタと同値)の下位5ビットをクリアする、ということですね。

次に進みます。
    129 	orr	r0, r0, #(I32_bit | F32_bit | PSR_SVC32_MODE)
orrは文字どおりorつまり論理和をとります。

先ほど下位5ビットをクリアしたr0にビットを立てています。I32_bit, F32_bit,PSR_SVC32_MODEのそれぞれです。これまたarm/include/armreg.hに定義されています。
     62 #define I32_bit (1 << 7)	/* IRQ disable */
     63 #define F32_bit (1 << 6)	/* FIQ disable */
     77 #define PSR_SVC32_MODE	0x00000013
コメントから想像するに、割り込みを禁止するビットのようですね。
    130 	msr	cpsr, r0
最初のmrs(リード)に対するmsr(ストア)ですね。ここまでr0を操作してきた内容をステータスレジスタにストアしています。

ということで、ここでは割り込みを禁止する一連の処理をしていたようです。

rpi_start.Sを読む (第1回)
2013/11/04(月) 20:47 NetBSD はてブ情報 はてブに登録 はてブ数

ARMアセンブリ言語を勉強するため、CPUのスタートアップを題材にソースコードを読んでみようと思います。

といっても、ARMアセンブリ言語の知識は皆無なので、Webで一命令ずつ調べながら、わからないところは大胆に飛ばしながら読んでいくという無責任企画です。

今話題のRaspberry Piをターゲットに、少しづつ勉強していきます。

src/sys/arch/evbarm/rpi/rpi_start.Sの1.7がターゲットになります。コメント含めて328行の小さなプログラムですが、自分にとっては楽しめそうです。

さて、はじめましょう。

一番最初に実行されるエントリーポイントから読んでいきます。ソースの最初の部分は少し飛ばして、122行目からスタートです。
    122 	.global	_C_LABEL(rpi_start)
    123 _C_LABEL(rpi_start):
ラベルですね。_CとついているとCのプログラムから呼べるんでしたっけ? 先に進みましょう。
    124 	adr	r8, rpi_boot_regs
いきなりのadrはGNU asの疑似命令です。指定したレジスタにラベル(ここだとrpi_boot_regs)のアドレスをロードします。プログラムカウンタr15にadd or subして実現されているそうです。r15なんて書かれてないから不思議な感じですね。

rpi_boot_regsには
    327 rpi_boot_regs:
    328 	.space 4 * 4
と書かれています。ブート用のレジスタ(?)の領域を確保ということかな? 4*4なので32bitのアドレスを4つ確保と読むのかな。

ということで、ラベルrpi_boot_regsのアドレスをr8に読み込むという命令ですね。

adrという疑似命令は、ARMでは命令の長さが4バイトに固定されるため、32ビット(4バイト)の整数 やラベルの値(メモリアドレス)を直接レジスタに転送することができません。 とのこと。

さて次に進みます。
    125 	stmia	r8!, {r0-r3}
stmはレジスタ-メモリ転送命令の複数版です。stmはストアマルチ、ldmはロードマルチかな。iaはIncrement Afterということで、メモリアクセス後にポインタを増やす。つまり、以下のようなイメージでしょうか。
  addr = r8;
  r0 = *(addr++);
  r1 = *(addr++);
  r2 = *(addr++);
  r3 = *(addr++);
 r8!の!は最後にr8に書き戻すという意味なので
  r8 = addr;
ということなのでしょう。

rpi_boot_regがr0からr3に割り当てられたということですね。

しかし、rpi_boot_regは、どこにも使っていそうな場所がありませんね。うーん。

PostgreSQL 9.3.1でレプリケーション(ただしNetBSD)
2013/11/03(日) 19:03 NetBSD はてブ情報 はてブに登録 はてブ数

PostgreSQLのコア機能にレプリケーションが実装されてずいぶん経過し、機能も着々と増強されています。

そんなレプリケーション機能をNetBSDユーザとしてあれこれ使ってみました。
  • ビッグエンディアンマシンとリトルエンディアンマシンでレプリケーションできるのか?
  • 32bitアーキテクチャと64bitアーキテクチャでレプリケーションできるのか?
  • Intel以外のアーキテクチャでレプリケーションできるのか?
以上の三本でお送りします。

ビッグエンディアンマシンとリトルエンディアンマシンでレプリケーションできるのか?
結論から言うと、できません。

ちょっと詳しい人なら、そりゃそうだろうなと思うはずです。
ただ、どのレベルで動かないか気になりますよね!

では、実機で検証してみましょう。

まず、ビッグエンディアンマシンを用意します。今回はMac mini (PPC版)を使いました。SPARCマシンを引っ張り出すのも良いでしょう。

次にリトルエンディアンマシンを用意します。ここでは、USL-5Pを使います。USL-5PはCPUにSH4を搭載しています。ここは国産CPUアーキテクチャに頑張っていただきましょう。

PostgreSQLのインストールは大変簡単で、pkgsrcを導入してpkgsrc/databases/postgresql93-serverでmake installと打つだけです。ただし、USL-5PはCPU 266MHz、メモリ64MB、ストレージがCFというスペックですので、distccなどを使ってコンパイルを進めたほうが良いでしょう。セットアップしてコンパイルするのに丸々一日程度を見込んでおけばよいと思います。

今回は、Mac miniをマスター、USL-5Pをスレーブとして起動します。

すると、次のようなエラーを出力してスレーブ側が起動しません。
FATAL:  database files are incompatible with server
DETAIL:  The database cluster was initialized with PG_CONTROL_VERSION -1459421184 (0xa9030000), but the server was compiled with PG_CONTROL_VERSION 937 (0x000003a9).
HINT:  This could be a problem of mismatched byte ordering.  It looks like you need to initdb.
なるほど、PG_CONTROL_VERSIONが(0xa9030000)と(0x000003a9)ですから、まさにエンディアン依存していますね。

近所のPostgreSQLコミッターに聞いたところ、このバージョンチェックを外したとしても、さまざまな場所でエンディアン依存しているから、クイックハックでどうにかできる代物ではない、とのコメントをいただきました。

ビッグエンディアンマシンを多く抱えるNetBSDユーザとしては、残念な結果となりました。
32bitアーキテクチャと64bitアーキテクチャでレプリケーションできるのか?
結論から言うとできません(二回目)。

ちょっと詳しい人なら、そりゃそうだろうなと思うはずです。
ただ、どのレベルで動かないか気になりますよね!(二回目)

では、実機で検証してみましょう。

64bitアーキテクチャとしてVMwareにNetBSD/amd64をインストールして32bitアーキテクチャであるUSL-5Pとレプリケーションさせてみます。

これまた、スレーブ起動時に次のようなエラーが表示されます。
FATAL:  incorrect checksum in control file
エラーメッセージで検索すると、64bitと32bitの違いに触れたページがいくつかヒットします。多くの場合は、マシンの移行に伴うもののようですが、レプリケーションも同様の理由で失敗するのでしょう。

エンディアンはともかくとして、チェックサムぐらいは同じにしてくれればいいのになとは思ったりするところです。

Intel以外のアーキテクチャでレプリケーションできるのか?
結論から言うと、できます。

ただし、先の例にもあるとおり、エンディアンが同じであったり、CPUのビット長が同じであるという前提条件を満たす必要があります。

今回は、32bitでリトルエンディアンをいくつかテストしました。

成功した組合せを列挙しておきます。
  • NetBSD/i386 on VMware(32bit)とNetBSD/landisk on USL-5P
  • NetBSD/i386 on VMware(32bit)とNetBSD/evbarm on Raspberry PI
Raspberry PIもPostgreSQLのコンパイルには時間がかかるので覚悟が必要ですね。
まとめ
PostgreSQLのコア機能によるレプリケーションを行う際には、エンディアンやビット長に注意する必要があることがわかりました。

しかし、エンディアンやビット長が違うマシン間でレプリケーションを行うことができないかというとそうではなく、マシン間でSQLを飛ばすタイプのSlony-Iを採用するなど、広い視点で検討を行う必要があるといえます。

また、今回の趣旨とは外れますが、PostgreSQLはIntel系のCPU以外でも問題なく動作することが確認できました。PowerPCでもARMでもSuperHでも動作しましたからね。今回は時間切れでMIPSは試せず、無念です。

なお、今回の実機検証ではNetBSDの6.1.2を使いました。