OS起動編① kinit1(end, P2V(4*1024*1024)) (Xv6を読む~OSコードリーディング~)

前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com


main.c(一部抜粋)
https://github.com/mit-pdos/xv6-public/blob/master/main.c#L20

int
main(void)
{
  kinit1(end, P2V(4*1024*1024)); // phys page allocator
  ...

kalloc.c
https://github.com/mit-pdos/xv6-public/blob/master/kalloc.c#L31

void
kinit1(void *vstart, void *vend)
{
  initlock(&kmem.lock, "kmem");
  kmem.use_lock = 0;
  freerange(vstart, vend);
}

Unix xv6では、カーネル空間のメモリ領域0x8011 5000〜0x8E00 0000をメモリ割り当てのために使用します。
そのための準備として、kinit1関数を呼び出し、0x8011 5000から0x8040 0000までのメモリ領域約4MBを使用可能な状態として管理します(使用可能なメモリページを把握するためのリストを作成します)。ここで使用可能な状態にしたメモリ領域約4MBは、次のkvmalloc関数の処理において、ページディレクトリ、ページテーブルを作成するために使用します。


処理の内容

initlock(&kmem.lock, "kmem");

initlock関数を呼び出して、メモリ管理のために使用するkmem構造体(使用可能なメモリページを把握するために用いられるリスト)に関わるロックを初期化しています。

kmem.use_lock = 0;

kmem構造体のロックをこれから使用する場合は1を、そうではない場合は0を設定します。

freerange(vstart, vend);

freerange関数を呼び出して、引数vstartで指定された仮想アドレスend→0x8011 44a8(freerange関数内で0x8011 5000に切り上げられる)から、引数vendで指定された仮想アドレスP2V(4*1024*1024)→0x8040 0000までのメモリ領域を使用可能な状態として管理します(使用可能なメモリページを把握するためのリストを作成します)。

endは、リンカスクリプトkernel.ldで定義されたシンボル(ラベル)で、メインメモリにロードされたカーネルプログラムにおけるbss領域の終端アドレスです。

kernel.ld

OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)

SECTIONS
{
	/* Link the kernel at this address: "." means the current address */
        /* Must be equal to KERNLINK */
	. = 0x80100000;

	.text : AT(0x100000) {
		*(.text .stub .text.* .gnu.linkonce.t.*)
	}

	PROVIDE(etext = .);	/* Define the 'etext' symbol to this value */

	.rodata : {
		*(.rodata .rodata.* .gnu.linkonce.r.*)
	}

	/* Include debugging information in kernel memory */
	.stab : {
		PROVIDE(__STAB_BEGIN__ = .);
		*(.stab);
		PROVIDE(__STAB_END__ = .);
	}

	.stabstr : {
		PROVIDE(__STABSTR_BEGIN__ = .);
		*(.stabstr);
		PROVIDE(__STABSTR_END__ = .);
	}

	/* Adjust the address for the data segment to the next page */
	. = ALIGN(0x1000);

	/* Conventionally, Unix linkers provide pseudo-symbols
	 * etext, edata, and end, at the end of the text, data, and bss.
	 * For the kernel mapping, we need the address at the beginning
	 * of the data section, but that's not one of the conventional
	 * symbols, because the convention started before there was a
	 * read-only rodata section between text and data. */
	PROVIDE(data = .);

	/* The data segment */
	.data : {
		*(.data)
	}

	PROVIDE(edata = .);

	.bss : {
		*(.bss)
	}

	PROVIDE(end = .);

	/DISCARD/ : {
		*(.eh_frame .note.GNU-stack)
	}
}

リンカスクリプトで定義されたシンボルは、ソースコード内でextern char end[] を用いる事で参照できます。
kalloc.c

extern char end[]; // first address after kernel loaded from ELF file
                   // defined by the kernel linker script in kernel.ld

その仮想アドレスの値は、0x8011 44a8です。

readelf -s kernel
286: 801144a8 0 NOTYPE GLOBAL DEFAULT 6 end

xv6実装の詳解(boot処理編: segmentationとpagingを中心に) - Qiita
https://gist.github.com/knknkn1162/09e9a8c12e0a4ea0db07deb1d7bb4c19#file-kernel_readelf_s-log-L291より引用


次回
jupiteroak.hatenablog.com