H8マイコンのエミュレータつくったはなし
この記事は、Aizu Advent Calendarの6日目として書かれました。
昨日 @misoton665 Kotlin(かわいい) - えびぞり。
明日 @youxkei
正確には、H8/3069FでMCU動作モードは、5のみです。
そして、すべての命令は網羅できていません。
先日[自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解!]http://uchanote.blogspot.jp/2015/08/x86.html)が出版され自分もなにか書いてみるかとことではじめました。
コードはgithub.com/himaaaatti/narcissusに上げています。
とりあえずの目標はKOZOSが動くことで、現状ステップ11までは動いてたり、動いていなかったり
H8/3069F
マニュアル(http://nsa.kpu-m.ac.jp/gijutu/h8/docs/h8_3069f.pdf)が公開されているので、ここから抜粋して 簡単にH8/3069Fについての説明を
概要
特徴
いろいろ上げても大変なのでここらへんに
流れ
命令が可変長なので、メモリから頭2 Byte読んでそれに応じて書いたり、読んだり
デバッグ
かすかにgdb remote debugができたり、できなかったり
(single step, break point, continue)
流石にもうすこし使えるようにいつかしたい
これから
- KOZOSが動くようになったら、一通りの命令は対応して、周辺機器対応も
- まじめに生きる
- がんばる
なにかあればgithubなりtwitterなりで連絡をお願いします。
コードに対するあれやこれなコメントも期待して待ってます
システムコールを呼べる様にするまで
この記事は、自作OS Advent calendar 23 日目です。
今回はsystem callについて書きました。sysenter/sysexitではなくintによる割込みゲートを使った実装です。
自分の自作OSは、IA-32,pentium 4とそれ以降のアーキテクチャ*1を想定しています。
なので今回もこれを前提に書きます。
idtについての簡単な知識があることを前提に書きますが、自分が理解していないところがあるかもしれせん。
記事の内容に関して、不明な点や間違いがあれば指摘してもらえればと思います。
自分のコードは、30日自作OS入門に影響をいろいろと受けていて、idtのエントリの登録もこの本に従っていました。 しかし、xv6やminixの割り込みの設定を見ると、idtに直接handlerを登録しておらず、 この理由として、ハードウェア依存を減らす為だと思っています。*2
基本的にxv6の実装を参考にしているので、その部分の説明をしたいと思います。
xv6の実装は、
vectors.plで生成されるvectors.Sに登録するhandlerがひたすらあって、
これらが、idtに登録されています。
vectors.Sの一部
.globl alltraps .globl vector0 vector0: pushl $0 pushl $0 jmp alltraps .globl vector1 vector1: pushl $0 pushl $1 jmp alltraps . . . .globl vector255 vector255: pushl $0 pushl $255 jmp alltraps # vector table .data .globl vectors vectors: .long vector0 .long vector1 . . . .long vector255
そのhandlerで何をしているかというと、
はじめの2個のpushで、trapframeの一部をstackを使って作っているのですが、
このtrapframeとはx86.hの中にある
struct trapframe { struct trapframe { // registers as pushed by pusha uint edi; uint esi; uint ebp; uint oesp; // useless & ignored uint ebx; uint edx; uint ecx; uint eax; // rest of trap frame ushort gs; ushort padding1; ushort fs; ushort padding2; ushort es; ushort padding3; ushort ds; ushort padding4; uint trapno; // below here defined by x86 hardware uint err; uint eip; ushort cs; ushort padding5; uint eflags; // below here only when crossing rings, such as from user to kernel uint esp; ushort ss; ushort padding6; };
で、vectorの一つ目のpushは
uint err;
の部分で、エラーコードがある例外の割り込みの際にはpushしていません。
例として14番のpage faultの例外はエラーコードが積まれるので
vector14: pushl $14 jmp alltraps
となっています。
uint err;
より下に定義してある、ものはCPUによって自動でスタックに積まれるので*3
ここでは特に触らない。
そして,alltrapsの実装はtrapasm.Sにあり
#include "mmu.h" # vectors.S sends all traps here. .globl alltraps alltraps: # Build trap frame. pushl %ds pushl %es pushl %fs pushl %gs pushal # Set up data and per-cpu segments. movw $(SEG_KDATA<<3), %ax movw %ax, %ds movw %ax, %es movw $(SEG_KCPU<<3), %ax movw %ax, %fs movw %ax, %gs # Call trap(tf), where tf=%esp pushl %esp call trap addl $4, %esp # Return falls through to trapret... .globl trapret trapret: popal popl %gs popl %fs popl %es popl %ds addl $0x8, %esp # trapno and errcode iret
trapframeの続きをスタックに積んでいき、
call trap の前にespをスタックにPushすることで、今まで積んだものをtrapframeとして
trapの引数として渡しています。
そして、実際の割り込みの動作をしているtrap関数です。
void trap(struct trapframe *tf) { if(tf->trapno == T_SYSCALL){ if(proc->killed) exit(); proc->tf = tf; syscall(); if(proc->killed) exit(); return; } switch(tf->trapno){ case T_IRQ0 + IRQ_TIMER: if(cpu->id == 0){ acquire(&tickslock); ticks++; wakeup(&ticks); release(&tickslock); } lapiceoi(); break; case T_IRQ0 + IRQ_IDE: ideintr(); lapiceoi(); break; case T_IRQ0 + IRQ_IDE+1: // Bochs generates spurious IDE1 interrupts. break; case T_IRQ0 + IRQ_KBD: kbdintr(); lapiceoi(); break; case T_IRQ0 + IRQ_COM1: uartintr(); lapiceoi(); break; case T_IRQ0 + 7: case T_IRQ0 + IRQ_SPURIOUS: cprintf("cpu%d: spurious interrupt at %x:%x\n", cpu->id, tf->cs, tf->eip); lapiceoi(); break; //PAGEBREAK: 13 default: if(proc == 0 || (tf->cs&3) == 0){ // In kernel, it must be our mistake. cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n", > tf->trapno, cpu->id, tf->eip, rcr2()); panic("trap"); } // In user space, assume process misbehaved. cprintf("pid %d %s: trap %d err %d on cpu %d " "eip 0x%x addr 0x%x--kill proc\n", proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, rcr2()); proc->killed = 1; } // Force process exit if it has been killed and is in user space. // (If it is still executing in the kernel, let it keep running // until it gets to the regular system call return.) if(proc && proc->killed && (tf->cs&3) == DPL_USER) exit(); // Force process to give up CPU on clock tick. // If interrupts were on while locks held, would need to check nlock. if(proc && proc->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER) yield(); // Check if the process has been killed since we yielded if(proc && proc->killed && (tf->cs&3) == DPL_USER) exit(); }
あとは、引数でもらったtrapframeのtrapnoを使ってそれぞれの処理に振り分けてます。
system_call()では中でeaxを使ってどのシステムコールなのかを特定していました。
自分の割込みの基本的な実装は同じなのですが、userとkernelのどちらからの割込みでも同じstackframeを
使っていることが気に入らなかったので、minixを参考に以下の様にしました。
#define IS_INT_IN_KERNEL(displ, label) \ cmpl $2, displ(%esp) ;\ je label
このマクロを使って
.globl system_call system_call: IS_INT_IN_KERNEL(12, system_call_by_kernel) system_call_by_user: #割込みの際に特権が変わった場合の処理(esp, ssが余分にstackに積まれる) system_call_by_kernel: push %eax call system_call_handler iret
今のところこのようにsystem callを実装しました。
実は、現状ユーザープロセスを動かせていないので、特権が変わるような割込みのテストができてないので
問題を抱えているかもしれません。
当初はsysenterだけ実装しようと思っていたのですが、intによるソフトウェア割込みと話が違いすぎたので
別の記事としていつか上げたられたらいいなと思います。
余談
qemuのi386-systemってCR4のpage-size extensionが使えることに驚きました。
参考
IA-32 インテル® アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、下巻: システム・プログラミング・ガイド http://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol3_i.pdf
Operating Systems Design and Implementation
寝袋の民
この記事はラボライフアドベントカレンダー、19日目の記事です。
突然思い立ったので参加させてもらいました。
今年ラボに配属されて半年ほどがたち、冬がやって来ました。 ここで問題が発生します。そう雪です。
今週の大寒波で大学へ来るのも命がけですよね、 登校と下校で命を落とすおそれがある
ということは、もう僕達に選択肢は2つしかありません。
大学に来ない or 大学から出ない
ですね。
僕には出席という使命があるのであるので大学から出ない方にしました。
ここから、やっと本題なのですが ラボに泊まるということで、寝袋がたまたま、お家にあったので持ってきてみました。 そして一回ダンボール敷いて寝てみたのですが、
いろいろ、つらかった
そこで、生活を快適にするグッズを紹介します。
長座布団 床の硬さと冷たさが改善されてso good
枕(クッション) 朝の首の痛さから解法されました。。
タオルケット これが真髄なのですが、2つの不満が解決しました。 一つ目に、僕が使っている寝袋が安物なので薄くて寒い、そもそも冬使うようなものでなかった。 もうひとつ、ビニール感じな素材なので汗とか吸うようなものではないのでで、少しでも汗を書くとはだによろしくなかった。
どれも某とりでお値段以上な感じでかつ、安くてにはいります。 それぞれ、1000 + 600 + 600 円ぐらいでした。 生活費を削ってどうにかなるレベルで良かったです。
この3つの神器によって今は快適な生活を送っています。
あなたも、寝袋という新しい可能性を見てみませんか?
brainf*ck!!
お昼ごろ思い立ってpython3でbrainf*ck書いてみた。
https://github.com/himaaaatti/brainf-ck
結構時間かかったのと、pythonの組み込み変数の'__debug__' と
assert文の使い方がわかったのでよかったです(こなみかん