090523

|



トマトは一花咲いてから定植と、本には書いてあった。蕾も見えたので来週あ
たりがよさそうだけれど、来週はレースの準備があるので植えることにしまし
た。

畝を作っている間に入浴。

ポットを外してみると、思ったほど毛根が生えてなかった。もっと心を鬼にして 水をあげなければよかったかな。

チンゲンサイに卵を見つけた。しかし今年は蝶がとても少ない。チンゲンサイ の成功もそこにあるかも。もう食用じゃないしこのまま餌にしようかなとも思っ たけれど、まわりの作物を考えて排除しました。

チンゲンサイの花。こうなるとアブラナ系なのを感じる。このまま種までとっ てみるつもり。

ネギはなんか食べられてます。不安。

トマトは参考書通りに一番目の本葉が土につくくらい斜めに植えました。 これ、夜になるともう茎が曲がって上を向いた。トマトは本当に面白い。

x86続き。

TSSについて。TSSに関わるEFLAGSのビットにはBとNTがある。Bは"Busy"。これ
は同じタスクに再入しないことを保証するためのもの。TSSの退避領域は一つし
かないので再入はできない。

NT"Nested Task"ビット、これはIRET命令とペア。IRET命令を発行した時に
NTビットが立っていれば、バックリンク先のTSSにタスクチェンジする。

割り込みハンドラでIRETを発行した時に、割りこまれたタスクではなくそれを
呼んだタスクまで戻ってしまうことを避けるために、割り込まれた時はCPUが
NTビットを退避してNT=0にして、割り込みハンドラを実行し、IRET終了後にNT
ビットを復帰することで、この問題を解決している。

x86のタスクまわりはこうすることにした。


   + 4GフラットのCS,DSをテンプレートに、そのタスクのセグメントは自分の
セグメントをLDTに持つ。(領域の設定はなし)
   + そのLDTはコード、データ、スタック、特権保護用のスタックの4つ。
   + スタックはそれぞれ小さくもつ。(そういうタスクなので)


タスクの設定
struct segment_config
{
  void *start;
  size_t size;
  uint16_t priority;
  uint16_t segment;
};

struct tss_config
{
  struct tss *tss; x86のTSS領域そのもの。
  uint16_t tss_segment; このTSS領域のセグメント。
  uint16_t gate_selector; このTSSへのゲート。
  int priority; タスクの準位。

  uint16_t code_segment; このタスクのアクセス領域へのテンプレート。このま
  まは使わない。

  uint16_t data_segment; このタスクのアクセス領域へのテンプレート。この
  ままは使わない。

  struct segment_config ldt; 上のテンプレートを元に設定したLDT。このタ
  スクはこのLDTを参照する。(CS,DS,ES,FS,GS)


  struct segment_config stack; タスクのスタック。

  struct segment_config stack_privilege; 特権準位を変更した時のスタック。
一個しか設定していないので、callできる準位は一つに限られる。(そういう
OSの設定)

  uint32_t eflags; タスク起動時のEFLAGS
  void (*func) (void); タスク起動時の入口。
  const char *name; これはTSSのOS依存部分に保存する。
};


#define	TSS_DEFAULT_STACK_SIZE	4096
#define	TSS_DEFAULT_LDT_NUM	4
#define	TSS_DEFAULT_LDT_SIZE	(4 * sizeof (union descriptor))

それぞれのタスクの必要とする領域。
struct task_local_storage
{
  struct tss tss;
  union descriptor ldt[TSS_DEFAULT_LDT_NUM];
  uint8_t stack[TSS_DEFAULT_STACK_SIZE];
  uint8_t stack_privilege[TSS_DEFAULT_STACK_SIZE];
} __attribute__ ((aligned (4096))); // struct tss limitation.
TSSがページ境界をまたぐとページングした時にフォルトするので、4096byte境界に
しておく。

struct task_local_storage task[5];
gdt_config.hと矛盾のないように。

STATIC struct tss bootstrap_tss; これはブートストラップの実行状態の退避
領域。ブートストラップは特権準位0なので、TSS領域から情報を読みとる(特権準
位の変更(上のみ)の時にスタックの変更する時にある)ことはない。


STATIC void task_user0 (void);
STATIC void task_user3 (void);

ダブルフォルト、TSS違反、スタックフォルト用の設定。
#define	EXCEPTION_TASK(n, cause, func)					\
    {									\
      &task[n].tss,							\
      GDT_TSS_ ## cause, GDT_GATE_ ## cause,				\
	0,	/* priority 0 */					\
      GDT_CODE32, GDT_DATA32,						\
      { task[n].ldt, TSS_DEFAULT_LDT_SIZE, 0, GDT_LDT ## n },		\
      { task[n].stack, TSS_DEFAULT_STACK_SIZE, 0, GDT_DATA32 },		\
      { 0, 0, 0, 0 },/* No privilege change. */				\
特権準位0なので、他の準位にコールゲートで移行することはないので設定する
必要がない。
      0,								\
      func, #cause							\
    }

#define	APPLICATION_TASK(n, app, func, priority)			\
    {									\
      &task[n].tss,							\
      GDT_TSS_ ## app, GDT_GATE_ ## app,				\
      priority,								\
      GDT_CODE32, GDT_DATA32,						\
      { task[n].ldt, TSS_DEFAULT_LDT_SIZE, priority, GDT_LDT ## n },	\
      { task[n].stack, TSS_DEFAULT_STACK_SIZE, priority, GDT_DATA32 },	\
      { task[n].stack_privilege, TSS_DEFAULT_STACK_SIZE, 0, GDT_DATA32 },\

stack_privilegeの特権準位を0にしているのはこのOSの仕様。コールゲートの
準位は0 だけということ。この準位はコールゲートから呼びだされるコードセ
グメントの準位と等しくしておかないといけない。低かった場合、スタックに
攻撃される可能性があるので。そういう設定をした場合はTSS例外でフォルトす
る。

      0,								\
      func, #app							\
    }

struct tss_config tss_config [] =
  {
    EXCEPTION_TASK (0, DOUBLE_FAULT, task_exception_entry),
    EXCEPTION_TASK (1, INVALID_TSS, task_exception_entry),
    EXCEPTION_TASK (2, STACK_OVERRUN, task_exception_entry),
    APPLICATION_TASK (3, TASK0, task_user0, 0),特権準位0のタスク
    APPLICATION_TASK (4, TASK3, task_user3, 3),特権準位3のタスク
  };

void
task_init ()
{
  struct tss_config *config;
  int i;

  // Setup my backup area.
  tss_install_gdt (&bootstrap_tss, GDT_TSS_KERNEL, GDT_GATE_KERNEL);
  strncpy (bootstrap_tss.osdep, "kernel", TSS_OSDEP_MAX);

  // Load myself. (no task change here.)
  tss_set_tr (GDT_TSS_KERNEL);
  printf ("%s: tss:0x%x size:0x%x\n", __FUNCTION__, &bootstrap_tss,
	  sizeof bootstrap_tss);

  // Setup TSS
  for (i = 0, config = tss_config; i < sizeof tss_config / sizeof tss_config[0];
       i++, config++)
    {
      memset (config->stack.start, 0xa5, config->stack.size);
      if (config->stack_privilege.start)
	memset (config->stack_privilege.start, 0xa5,
		config->stack_privilege.size);

  TSSの内容を作って、
      tss_setup (config);
  GDTにそのTSS領域を設定し、それを呼ぶゲートを設定する。
      tss_install_gdt (config->tss, config->tss_segment, config->gate_selector);
    }

  tss_dump (GDT_TSS_KERNEL);
  tss_dump (GDT_TSS_TASK0);
  tss_dump (GDT_TSS_TASK3);
  // Now OK to use gate.
  tss_call (GDT_GATE_TASK0);
  tss_call (GDT_TSS_TASK0);

  IRETで帰ってくる。IRETによってTASK0のTSSのEIPにはIRET命令の次の命令が
設定されるので、次にタスクを呼んだ時にはそこからになる。
}

void
tss_setup (const struct tss_config *conf)
{
  struct tss *tss = conf->tss;
  int priority = conf->priority;
  const struct segment_config *seg;

  memset (tss, 0, sizeof (struct tss));

  このタスクのCS,DS,ES,FS,GSは全てここでセットアップされたLDTを参照する。
  // Construct my LDT.
  tss->ldt = ldt_setup (conf);

  // Stack for privilege change.
  seg = &conf->stack_privilege;
  tss->esp0 = (uint32_t)seg->start + seg->size - 16;
  tss->ss0 = 24 | SELECTOR_TI | seg->priority; LDTを参照。24はそう決めた。
  このプライオリティはコールゲート呼びだされる準位でないといけない。
  この場合、seg->priorityは0でないといけない。
  tss->ss1 = tss->ss2 = 0;	// unused.
  tss->esp1= tss->esp2 = 0;	// unused.

  // Normal stack.
  seg = &conf->stack;
  tss->eip = (uint32_t)conf->func;
  tss->eflags = conf->eflags;
  tss->esp = (uint32_t)seg->start + seg->size - 16;

  // Code/Data segment.
  tss->cs = 8 | SELECTOR_TI | priority;  LDTを参照。8はそう決めた。
  tss->ds = tss->es = tss->fs  = tss->gs = tss->ss =
    16 | SELECTOR_TI | priority; LDTを参照。16はそう決めた。

  // IOPL
  tss->iomap = offsetof (struct tss, io_mask);
  tss->io_mask[TSS_IO_MASK_MAX - 1] |= 0xff000000;	// Marker.

  // OS depenedent.
  strncpy (tss->osdep, conf->name, TSS_OSDEP_MAX);
  tss->osdep[TSS_OSDEP_MAX - 1] = '\0';
}

タスクが参照するLDTの設定

uint16_t
ldt_setup (const struct tss_config *conf)
{
  union descriptor *d;
  union descriptor *ldt = (union descriptor *)conf->ldt.start;
  int priority = conf->priority;

  このLDT自体の場所をGDTに登録します。
  // LDT itself.
  d = descriptor (conf->ldt.segment);
  descriptor_set_base (&d->desc, (uint32_t)conf->ldt.start);
  descriptor_set_size (&d->desc, conf->ldt.size);
  descriptor_set_priority (d, priority);

  テンプレートをLDTにコピーして特権準位を設定します。

  LDTを使っているのは、テンプレートはメモリの領域だけの設定にしておいて、
  そのコピーに対して各自特権準位を設定したいため。GDTは保護モードになっ
  てすぐ設定したいので、先にスタティックに作っておきたいけれど、LDTは楽
  に作れるのでこういう設定。

  // Copy code segment.
  d = descriptor (conf->code_segment);
  ldt[1] = *d;
  descriptor_set_priority (ldt + 1, priority);// Set DPL
  // Copy data segment. (including stack)
  d = descriptor (conf->data_segment);
  ldt[2] = *d;
  descriptor_set_priority (ldt + 2, priority);// Set DPL

  // Copy stack segment for privilege change. (If any.)
  if (conf->stack_privilege.segment)
    {
  タスクの特権準位が0なら、コールゲートで特権準位変更をすることはできな
  いのでこの処理はいらない。使われないので。
      d = descriptor (conf->stack_privilege.segment);
      ldt[3] = *d;
      descriptor_set_priority (ldt + 3, conf->stack_privilege.priority);

  このconf->stack_privilege.priorityは、コールゲートで指定される特権準
位。
    }

  return conf->ldt.segment;
}