www.mtkn.jp

Manuscripts for my personal webpage.
git clone https://git.mtkn.jp/www.mtkn.jp
Log | Files | Refs | LICENSE

commit 0e66aab9411b9de064f9c1085dc72ac4c210d575
parent 6b1de14205678f72a6fa49faa81897c1aa420694
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Mon,  3 Nov 2025 11:00:42 +0900

update smtpd.conf

Diffstat:
Mdata/weblog | 4++++
Mman/computer/mailserver.html | 7+++++--
Mpub/computer/mailserver.html | 7+++++--
Mpub/rss.xml | 4053++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mpub/sitemap.xml | 2+-
5 files changed, 2043 insertions(+), 2030 deletions(-)

diff --git a/data/weblog b/data/weblog @@ -114,3 +114,7 @@ 1756220400 /computer/rp2040_3_interrupt.html 1756220400 /index.html 1756220400 /index.html +1762095600 /computer/mailserver.html +1762095600 /computer/mailserver.html +1762095600 /computer/mailserver.html +1762095600 /computer/mailserver.html diff --git a/man/computer/mailserver.html b/man/computer/mailserver.html @@ -1,5 +1,6 @@ <h1>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</h1> -<time>2024-01-19</time> +<time>2024-01-19</time><br> +<time>2025-11-03</time> <code>HELO</code>コマンドで表示されるドメイン名を変更<br> <h2>はじめに</h2> <p> @@ -533,7 +534,9 @@ action &quot;local_mail&quot; mbox alias &lt;aliases&gt; action &quot;domain&quot; maildir &quot;/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}&quot;\\ virtual &lt;virtuals&gt; # 他のドメイン宛ては宛先へ転送。 -action &quot;outbound&quot; relay +# 他のsmtpサーバーとの接続を確立する際にデフォルトではこのコンピュータのホスト名(僕の場合"sakura.mtkn.jp")で +# 自己紹介しており、DNSレコードが見付からないとおこられていたため helo "mtkn.jp" を追加 +action &quot;outbound&quot; relay helo "mtkn.jp" # どういう条件でどういう処理をするかを定義する。 # 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。 diff --git a/pub/computer/mailserver.html b/pub/computer/mailserver.html @@ -24,7 +24,8 @@ <main> <article> <h1>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</h1> -<time>2024-01-19</time> +<time>2024-01-19</time><br> +<time>2025-11-03</time> <code>HELO</code>コマンドで表示されるドメイン名を変更<br> <h2>はじめに</h2> <p> @@ -421,7 +422,9 @@ action &quot;local_mail&quot; mbox alias &lt;aliases&gt; action &quot;domain&quot; maildir &quot;/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}&quot;\ virtual &lt;virtuals&gt; # 他のドメイン宛ては宛先へ転送。 -action &quot;outbound&quot; relay +# 他のsmtpサーバーとの接続を確立する際にデフォルトではこのコンピュータのホスト名(僕の場合"sakura.mtkn.jp")で +# 自己紹介しており、DNSレコードが見付からないとおこられていたため helo "mtkn.jp" を追加 +action &quot;outbound&quot; relay helo "mtkn.jp" # どういう条件でどういう処理をするかを定義する。 # 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。 diff --git a/pub/rss.xml b/pub/rss.xml @@ -5,8 +5,8 @@ <description>ウェブページの更新履歴</description> <language>ja-jp</language> <link>https://www.mtkn.jp</link> -<lastBuildDate>Wed, 27 Aug 2025 09:58:55 +0900</lastBuildDate> -<pubDate>Wed, 27 Aug 2025 09:58:55 +0900</pubDate> +<lastBuildDate>Mon, 3 Nov 2025 10:59:19 +0900</lastBuildDate> +<pubDate>Mon, 3 Nov 2025 10:59:19 +0900</pubDate> <docs>https://www.rssboard.org/rss-specification</docs> <item> <title>9P</title> @@ -372,2442 +372,2445 @@ size[4] Rwstat tag[2] ]]></description> </item> <item> -<title>RP2040 SDKなし3 割り込み</title> -<link>https://www.mtkn.jp/computer/rp2040_3_interrupt.html</link> -<guid>https://www.mtkn.jp/computer/rp2040_3_interrupt.html</guid> -<pubDate>Wed, 27 Aug 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>RP2040 SDKなし3 割り込み</h1> -<time>2025-08-27</time><br /> -<p> -前回UARTで通信する際、FIFOにデータが届いていないかを確認しつづけていた(ポーリング)。 -これでは電力がもったいないので、データが届くまでCPUは休ませておいて、 -届いたときだけ処理を実行するように変更する(割り込み)。 -<p> +<title>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</title> +<link>https://www.mtkn.jp/computer/mailserver.html</link> +<guid>https://www.mtkn.jp/computer/mailserver.html</guid> +<pubDate>Mon, 3 Nov 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</h1> +<time>2024-01-19</time><br> +<time>2025-11-03</time> <code>HELO</code>コマンドで表示されるドメイン名を変更<br> + +<h2>はじめに</h2> <p> -前回: <a href="rp2040_2.html">RP2040 SDKなし2 Clock、UART</a><br> -ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex3_interrupt -</p> +少し前にさくらのVPSで独自ドメインのメールサーバーを構築した。当時はドキュメントを追うのに必死で構築手順を整理できていなかった。しかしスマホを使わない僕の数少い連絡手段なので、受信できなくなると困る。そこであらためてサーバーを構築し、その手順を確認することにした。あいかわらずDNSの知識は自信がない。セキュリティは自己責任で。</p> -<h2>動作環境</h2> +<h2>使うもの</h2> <ul> -<li>OpenBSD 7.7 - <ul> - <li>arm-none-eabi-binutils-2.40</li> - <li>OpenBSD Make</li> - <li>minicom version 2.8</li> - </ul> -</li> -<li><a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a> -</li> +<li>VPS: さくらのVPS (IPアドレスを<i>sss.sss.sss.sss</i>とする)</li> +<li>OS: OpenBSD 7.4</li> +<li>ドメイン: ムームードメイン (mtkn.jpとする)</li> +<li>SMTPサーバー(MTA): OpenSMTPD 7.4.0</li> +<li>IMAPサーバー(MDA): Dovecot 2.3.20</li> </ul> -<h2>割り込み</h2> +<h2>メールの仕組み</h2> <p> -ハードウェアが外部から信号を受けとると、CPUに現在の処理を中断させてその信号を処理させるようにできる。これを割り込み処理という。今回はUARTにデータが届いた時に同じデータをUARTに書き込むようにしたい。</p> - -<h3>割り込みを有効化</h3> +メールの送受信にはSMTPというプロトコルが使われ、この通信を担うのがSMTPサーバーと呼ばれるソフトウェアである。このサーバーはメールの転送を担うので、MTA(Mail Transfer Agent)とも呼ばれる。今回はMTAとしてOpenSMTPDというのを使う。手元のパソコン等で書いたメールは、SMTPの通信で送信側のMTAに送られ、そこから受信側のMTAに転送される。受信側のMTAでは届いたメールをユーザーごとにふりわけ、所定の場所に保存する。</p> <p> -割り込みの信号を受け取るために、割り込みを有効化する。CPUが割り込みを受け付けるようにする設定と、UARTが割り込み信号を発生させるようにする設定が必要である。</p> +受信側のユーザーは、手元のパソコン等から受信側のサーバーに繋ぎ、自分宛てのメールを確認する。この時に使われるのがIMAPというプロトコルである(POPというのもあるが今回は割愛)。IMAPによるこの通信を担うのがIMAPサーバーで、メールを配達する役割を担うことから、MDA(Mail Delivery Agent)とも呼ばれる。今回MDAとしてDovecotを使用する。</p> <p> -CPU側の設定は<code>M0PLUS: NVIC_ISER</code>レジスタの有効化したい割り込みのビットに1を書くことで行う。有効化できる割り込みは全部で26個ある。割り込みの種類はrp2040のデータシート[1]のTable 80. Interruptsに書いてある。今回有効化するのはUART0の割り込みで、この表から20番が割り当てられていることが分かるので、<code>M0PLUS: NVIC_ISER</code>に、下から20番目のビットを1にしたもの書き込む:</p> -<code><pre> // enable uart interrupt in cpu - ldr r4, ppb_base - mov r5, #0xe1 - lsl r5, #8 - mov r0, #1 - lsl r0, #20 - str r0, [r4, r5] // M0PLUS: NVIC_ISER +以上をまとめると以下のようになる: +</p> +<pre> ++------------+ +----------------+ +|MTA(mtkn.jp)|--SMTP-&gt;| MTA | ++------------+ +--example.com---+ + ^ | MDA | + | +----------------+ + | | + SMTP IMAP + | | + | v ++------------+ +----------------+ +|user@mtkn.jp| |user@example.com| ++------------+ +----------------+ -/* ... */ +</pre> +<ol> +<li>メールを書く。</li> +<li>送信側のMTAに送る。</li> +<li>受信側のMTAに転送する。</li> +<li>受信者が受信側のMDAに新着メールの有無を確認する。</li> +</ol> -ppb_base: - .word 0xe0000000 -</pre></code> <p> -UART側の設定は、<code>UART: UARTIMSC</code>レジスタで行なう。データを受け取ったときに割り込みを発生させたいので、<code>RXIM</code>ビットに1を書き込む。また、処理を開始する前に既にFIFOにデータがたまっている可能性があるので、FIFOが空ではないときに割り込みを発生させるように、<code>RTIM</code>にも1を書き込む:</p> -<code><pre> // enable uart interrupt in uart0 subsystem - ldr r1, uart0_base - mov r2, #(1 &lt;&lt; 6 | 1 &lt;&lt; 4) // RTIM | RXIM - str r2, [r1, #0x38] // UART: UARTIMSC -</pre></code> +SMTPサーバーどうしが通信する際、スパムメールをフィルタリングする為、いくつかの方法で送信元の本人確認を行う。その際第三者としてDNS及び、サーバーのIPアドレスを管理している人(今回の場合はさくらインターネット)が登場する。DNSにはドメインをIPアドレスにひもづける情報が登録できる。またIPアドレスの管理人には、自分が借りているIPアドレスを自分のドメインにひもづける情報を登録してもらう。これらによって、メールの送信者がドメイン、IPアドレス双方から確認できるので、なりすましを防止できる仕組みである。</p> + <p> -また、FIFOにデータがきたらすぐに割り込みを発生させたいので、<code>UART: UARTIFLS</code>レジスタの<code>RXIFLSEL</code>に0を書き込む。これでFIFOの1/8が埋まったら割り込みが発生する。</p> -<code><pre> // set fifo level to 0 - mov r2, #0 - str r2, [r1, #0x34] // UART: UARTIFLS -</pre></code> +最近はgmail等の大手がスパム対策を強化する一環として、以下に述べるような本人確認をすべて通らなければ受信しないようにしているらしい。</p> + +<h2>MTAどうしのやりとり</h2> <p> -これだとFIFOに届いた瞬間には割り込みがおこらない気がするが、pico-sdk(src/rp2_common/hardware_uart/include/hardware/uart.h)の<code>uart_set_irqs_enabled</code>関数でもこうなってる。FIFOを無効にすれば即座に割り込みがおこるかもしれないが、データが失われる可能性もあるのかな。</p> +以下、MTAどうしの通信内容をざっくり説明する。 +</p> -<h3>ベクターテーブルの設定</h3> +<h3>Aレコード、MXレコードによる送信先の特定</h3> <p> -外部からの信号で割り込みが発生したときにCPUに行わせる処理はベクターテーブルにあらかじめ登録しておく必要がある。ベクターテーブルの場所は<a href="rp2040_1.html">一回目</a>に登録した通り<code>vectors</code>とラベルを付けた場所(main.sの先頭)である。ARMの仕様書[2]によると、ベクターテーブルの0番目は初期のスタックポインタで、そこから15個はARMによって使用用途が定められている。16番以降の32個は各CPUの設計者が自由に決められる。rp2040の割り込みについてはデータシート[1]のTable 80. Interruptsに一覧表が載っている。今回使いたいのはUART0の割り込みなので16 + 20 = 36番目に、割り込み時に呼ばれたい関数のポインタ(Thumb Modeなので関数のアドレスに1を足したもの)を書き込めばいい。それ以外のものは今回は使わないので適当な数値でうめておく:</p> -<code><pre> .global vectors -vectors: - .word 0x20040000 // initial SP - .word (reset+1) // entry point - .word 0xdeadbeef - .word 0xdeadbeef +MTAがメールを送信する際、まずは宛先のサーバーを特定する必要がある。この際に使用するのがDNSに登録されているMXレコードである。送信元のMTAはまずDNSに宛先のドメインのMXレコードを問い合わせる。するとメールの送信先のドメイン名が分かるので、続いてこの送信先のIPアドレスを問い合わせる:</p> +<pre> +MTA (mtkn.jp <i>sss.sss.sss.sss</i>) DNS - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef + example.com宛のメールって + 誰に送ったらええん? + ----------------------------------------&gt; - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef + mai.example.comやで(MXレコード) + &lt;---------------------------------------- - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef + mail.example.comってどこや? + ----------------------------------------&gt; + <i>ddd.ddd.ddd.ddd</i>やで(Aレコード) + &lt;---------------------------------------- +</pre> +<p> +次にこの宛先のMTAとの通信を開始する。しかし受信側のMTA(example.com)は、mtkn.jpを名乗るサーバーが本当にmtkn.jpかどうか確認する必要がある:</p> +<pre> +MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>) + こんちは。mtkn.jpです。 + ----------------------------------------&gt; - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef + なりすましちゃうか確認したろ +</pre> - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef +<h3>SPFレコードによる送信元の本人確認</h3> +<p> +受信側のMTAは、自分に連絡してきたサーバーが本人かどうかをDNSに問い合わせる。このときに使うのがSPFレコードである。このレコードにはドメインの所有者が、自分のドメインからメールを送信してもいいIPアドレスを指定する。受信側のMTAは送信元のIPアドレスと、このレコードに記載されているIPアドレスを比べることで、送信側のMTAがドメインの所有者に認められているMTAかどうか判断できる。</p> +<pre> +DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) + mtkn.jpを名乗るやつから連絡 + 来てるんやけどこいつ本物? + &lt;---------------------------------------- - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef + IPアドレスが<i>sss.sss.sss.sss</i> + やったら多分本物やわ。(SPFレコード) + ----------------------------------------&gt; - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef + よっしゃ +</pre> - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef - .word 0xdeadbeef +<h3>PTRレコード</h3> +<p> +次は逆にIPアドレスからドメインを辿って本人確認する。これに使われるのがPTRレコードである。このレコードはIPアドレスの管理人(ここではさくらインターネット)でないと登録できない。しかし、IPアドレスを借りるにあたって、自分の名前やら住所やらを提供しており、PTRレコードをきちんと設定した上でスパムメールを送ると、自分の身元がバレるので、スパムの抑止に繋がる。</p> +<pre> +DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) + <i>sss.sss.sss.sss</i>ってIPアドレスで + mtkn.jpってドメイン登録されてる? + &lt;---------------------------------------- - .word (uart_interrupt_handler+1) // UART0_IRQ -</pre></code> + されてるで。(PTRレコード) + ----------------------------------------&gt; -<h3>割り込み処理</h3> + よっしゃ +</pre> + +<h3>DKIM</h3> <p> -割り込み発生時に呼ばれる関数はUARTのFIFOに届いたデータをそのまま送信するだけのものである:</p> -<code><pre> // uart_interrupt_handler echos the data arrived at uart0 -uart_interrupt_handler: - push {lr} - bl getbyte - bl putbyte - pop {pc} -</pre></code> +送信側MTAが本人であることを証明するため、受信側に送るメールに署名する。署名は公開鍵方式で行うが、この鍵をドメインキーというようである。この署名が付いたメールをDKIM(DomainKey Identified Mail)という。送信側はあらかじめDNSにドメインキーの公開鍵を登録しておき、メール送信時に秘密鍵で署名する:</p> +<pre> +MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>) + + 本人確認できたわ + おまたせ。 + &lt;---------------------------------------- + + userさんにメール送るで。 + 署名も付けるで。 + ----------------------------------------&gt; +</pre> <p> -<code>getbyte</code>、<code>putbyte</code>は前回と同じもの。 -</code> +受信側はDNSから送信側のドメイン鍵の公開鍵をダウンロードし、署名を確認する:</p> +<pre> +DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) + mtkn.jpが署名付きでメール + くれたんやけどこの署名本物? + &lt;---------------------------------------- -<h2>メインループ</h2> + これで確認してみて(DKIM公開鍵) + ----------------------------------------&gt; + + よっしゃ +</pre> + +<h3>DMARCレコード(上記の本人確認で詐欺だと発覚した場合)</h3> <p> -UARTにデータが届いていないときは特にすることがないのでCPUは寝かせておく。ARMには割り込みが発生するまでなにもしない<code>wfe</code>という命令があるのでこれを使う:</p> -<code><pre>loop: - wfe - b loop -</pre></code> +この他DNSにはDMARCレコードというものを登録することができる。これは、以上の本人確認により送信側がにせものであることが確認できた際、受信側にどのような行動を取ってほしいかを記述するものである。これにより例えば、自分を名乗る詐欺師がどこかにメールを送信した場合、受信したサーバーから自分宛てに通報するように頼める(この頼みが聞き入れられるかどうかは多分受信側MTAによる)。 +</p> +<pre> +DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) + mtkn.jpを名乗る詐欺師から + メールきたんやけどどないしょ? + &lt;---------------------------------------- -<h2>完成</h2> + ここ(postmaster@mtkn.jp)に通報や。 + (DMARCレコード) + ----------------------------------------&gt; + + よっしゃ +</pre> + +<h2>サーバーの設定</h2> +<h3>VPSの契約、ドメインの取得</h3> <p> -これで完成。動作は前回と全く同じだが、消費電力をおさえられる。</p> - -<h2>参考</h2> -<ul> -<li> -[1]. <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> -</li> -<li> -[2]. <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> -</li> -<li> -[3]. <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> -</li> -</ul> -]]></description> -</item> -<item> -<title>自画像</title> -<link>https://www.mtkn.jp/gallery/20250717.html</link> -<guid>https://www.mtkn.jp/gallery/20250717.html</guid> -<pubDate>Thu, 17 Jul 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>自画像</h1> -<time>2025-07-17</time> -<img src="img/20250717.jpg"> -]]></description> -</item> -<item> -<title>万博で知り合ったルーマニア人とハイキングに行った</title> -<link>https://www.mtkn.jp/journal/posts/20250519.html</link> -<guid>https://www.mtkn.jp/journal/posts/20250519.html</guid> -<pubDate>Mon, 19 May 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>万博で知り合ったルーマニア人とハイキングに行った</h1> -<time>2025-05-19</time> +VPSとドメインを契約する。今回はVPSをさくらインターネットで、ドメインをムームードメインでそれぞれ契約した。さくらのVPSは好きなISOからOSをインストールでき、管理画面も分かりやすい(他のVPSを知らないので比較はできないが)。ムームードメインは安い。管理画面も悪くはない。以前お名前.comでも借りたことがあるが、こちらは広告のメールがやたら届いてうっとうしかった記憶がある。</p> +<h3>OpenBSDのインストール</h3> <p> -先日万博のルーマニアパビリオンで知り合ったルーマニア人の<a href="https://noian.art">Noian</a>がハイキングに行きたいというので、地元の山に連れていった。元々4月の終わりごろを予定していたが、僕が風邪をひいてしまいのびていた。向こうも万博の仕事が忙しいようで、帰国の前日になってやっと予定が合った。</p> - +さくらのVPSにOpenBSDをインストールする。さくらインターネットが用意したISOがあるので、OS再インストールの画面からそれを選べばすぐにインストールきる。でもなんとなく気持悪いのでopenbsd.orgから本家をダウンロードした。</p> <p> -10時過ぎにうちの最寄り駅に着く予定だったが、電車に乗り間違えたらしく一時間程遅れた。なにせ阪和線である。外国人にはかなりややこしい。そのうえ駅員はフランス人のごとく英語を話さない。間違って関空に行かないようにしつこく注意喚起して、なんとか和歌山に辿りついた。</p> - +インストールのこまかい手順は割愛するが、ひとつだけひっかかった点があるので書いておく。インストール先のディスクをセットアップし、OSに必要なファイルをインストールメディアからディスクにコピーすると以下のエラーがでてカーネルパニックになった: +</p> +<pre><code>wdc_atapi_start: not ready, st = 50 +fatal protection fault in supervisor mode +trap type 4 code 0 rip ffffffff810081a9 cs 8 rflags 10282 cr 2 23a896000 cpl 6 rsp ffff80000e9df410 +gsbase 0xffffffff81908ff0 kgsbase 0x0 +panic: trap type 4, code=0, pc=ffffffff810081a9 +syncing disks...18 18 18 18 18 18 18 18 +</code></pre> <p> -車で自宅まで連れて来て、そこから徒歩で山に向かった。自然が好きそうだったので、ろくに整備されていないひとけのない山に案内した。</p> +原因はあまり調べていないが、どうもインストールメディアの読み込みに失敗しているようで、インストールに必要なファイルをウェブ上からダウンロードする方法を選択すると問題なくインストールできた。 +</p> +<h3>ファイアーフォールの設定</h3> <p> -この山に登るのは久しぶりなので道を覚えているか不安だった。おそらく昔の人が隣の町まで行くのに使っていた道だと思う。今でも一応隣町まで抜けられる。入口は果樹園になっていて今も柑橘の木がたくさん植わっている。川沿いを森に入ると左右には棚田が並んでいる。こちらの田んぼは既に放棄され、今は藪や針葉樹林になっている。針葉樹の方は戦後の拡大造林で植えられたものだろうか、毎年ティッシュペーパーの需要を底上げしている木々である。棚田は石垣で造成されている。いつのものかは知らないが今も崩れずに残っている。</p> - +メールの送受信等に必要なポートを開ける:</p> +<ul> +<li>TCP 22: ssh接続用</li> +<li>UDP 53: DNSとの通信用</li> +<li>TCP 25: SMTPの通信用</li> +<li>TCP 587: メールクライアントからMTAへの通信用(メール送信時)</li> +<li>TCP 993: メールクライアントからMDAへの通信用(メール受信時)</li> +<li>TCP 80, 443: <code>acme-client(1)</code>によるサーバー証明書の取得用</li> +</ul> <p> -棚田の横をさらに進むと小さい滝がある。牝牛の滝というらしい。牝牛と書いてコッテと読む。滝の近くに牝牛に見える岩があるのが由来らしい。コッテという言葉は聞き馴染みがない。後日祖父の友人に聞いた話だと、小さい牛やメスの牛のことをコッテウシと言ったそうだ。</p> - -<figure> -<img src="20250519-1.jpg" alt="牝牛の滝"> -<figcaption><ruby>牝牛<rp>(</rp><rt>コッテ</rt><rp>)</rp></ruby>の滝</figcaption> -</figure> +以上の設定を<code>/etc/pf.conf</code>に設定する: +</p> +<pre><code>set skip on lo -<p> -滝を過ぎると急勾配の斜面が続く。ほとんど道とはいえないような山肌を登る。40度はありそうな勾配である。息があがりながらも、彼は日本に来たがっている友人のことを聞かせてくれた。日本のとあるバンドの大ファンなのだそうだ。ポップカルチャーにはうといので自国のことなのになにも分からなかった。最後に大きな岩が作った崖を山羊のように登ればやっと平坦な道に出る。彼のブーツがよく滑っていた。裸足が一番安全だと勧めたが遠慮しとくと言われた。そこから先は特にきつい場所もなく、そのまま展望台に続く。彼は少し疲れたようで言葉が少なくなっていた。</p> +block return # block stateless traffic +pass in log proto tcp from any to any port 22 #ssh -<figure> -<img src="20250519-2.jpg" alt="展望台から"> -<figcaption>展望台から</figcaption> -</figure> +pass out proto udp from any to <i>DNSのIPアドレス</i> port 53 #DNS +pass out proto udp from any to any port 123 #ntp +pass proto icmp #ping +pass proto tcp from any to any port { 80, 443 } #www +pass proto tcp from any to any port { 25, 587, 993 } #mail +</code></pre> <p> -展望台からは僕の町が一望できる。西が海で南北が山、東はゆるがかな坂が高野山まで続いている。彼の出身地も山がちで似た地形だそうだ。海はないが毎年父親が黒海まで連れていってくれていたので馴染深いといっていた。展望台には小さな祠がある。せっかくなので鳥居のくぐり方や参拝の仕方を教えてあげた。熱心に聞いてはくれたが自分はやらないと言っていた。理由は聞かなかったがおそらく彼がキリスト教徒だからであろう。こういうはっきりした態度は見ていてなんとなく気持がいい。</p> +<code>pf(4)</code>の設定を反映: +</p> +<pre><code># pfctl -f /etc/pf.conf +</code></pre> +<h3>DNSの設定</h3> +<h4>A, MX, SPF, DMARC</h4> <p> -ところで日本では神道でも仏教でも左進右退が基本だと思う。以前五瀬命が祀られている竃山神社のお祭に行ったことがある。そこでは巫女さんが神殿に向う階段を登る際左足を一段上げて右足を引き寄せてを繰り返して登り、下るときは逆にしていた。お寺でも道場に入るときは左から、出るときは右からと習った。そういえば合気道でも座礼をするとき左手、右手の順で床に付けて頭を下げ、頭を上げるときは右手から戻すと習った気がする(この記憶はあいまい)。ところがヨーロッパでは逆らしい。左は邪悪だから右から入るのだそうだ。</p> +DNSのレコードを設定する。ムームーDNSの場合、コントロールパネルにログインし、サイドバーからドメイン管理&gt;ドメイン操作&gt;ムームーDNSと進み、設定するドメインの変更ボタンをクリックすると設定画面がでるので、以下のように設定する:</p> +<table> +<thead> +<tr> +<th>サブドメイン</th> +<th>種別</th> +<th>内容</th> +<th>優先度</th> +</tr> +</thead> +<tbody> +<tr> +<td></td> +<td>A</td> +<td><i>sss.sss.sss.sss</i></td> +<td></td> +</tr> +<tr> +<td>mail</td> +<td>A</td> +<td><i>sss.sss.sss.sss</i></td> +<td></td> +</tr> +<tr> +<td></td> +<td>MX</td> +<td>mail.mtkn.jp</td> +<td>10</td> +</tr> +<tr> +<td></td> +<td>TXT</td> +<td>v=spf1 mx -all</td> +<td></td> +</tr> +<tr> +<td>_dmarc</td> +<td>TXT</td> +<td>v=DMARC1;p=reject;rua=mailto:postmaster@mtkn.jp</td> +<td></td> +</tr> +</tbody> +</table> <p> -下りは別の道を選んだ。途中からコンクリートで舗装されている楽な道である。途中で山菜がいろいろ生えていたので紹介してあげたらぱくぱく食べていた。よく信用できるものである。イタドリ、ユキノシタ、フキ、チャノキ、タラノキ...。タケノコはもう竹だった。クスノキもあったのでいい香りがすると言って渡したらこれもかじっていた。クスノキが食べられるかどうか僕は知らない。防虫剤に使われていたと伝えるとすぐに吐きだして水で口をすすいでた。</p> - +ひとつめのAレコードは、ドメイン名(mtkn.jp)にIPアドレス(<i>sss.sss.sss.sss</i>)を紐付けるものである。ふたつめはmail.mtkn.jpのIPアドレスを登録するものである。みっつめのMXレコードは、このドメイン宛てのメール(user@mtkn.jp等)をどこのサーバーに送信するかを決めるものである。上記の設定では、mail.mtkn.jpに送信するようにしている。以上の設定で、user@mtkn.jp宛てのメールは、mail.mtkn.jpというサーバーに送信すればよいことが分かり、さらにこのサーバーのIPアドレスは<i>sss.sss.sss.sss</i>であることも分かる。 +</p> <p> -展望台でフルートを吹く動画を撮りたかったのに忘れていたといっていたが、途中で見つけた竹林を気に入ったようで、そこで吹いていた。このフルートはチューニングが現代の一般的なものとは違い、ミの音が少し低くなっている。長調とも短調ともいえない微妙な音階だった。</p> +次のTXTレコードはSPFレコードである。v=spf1でSPFのバージョンを示し、mxでドメインのMXレコードに登録されたIPアドレスからの送信を許可し、-allでそれ以外のIPアドレスからの送信を禁止している。 +</p> +<p> +最後のものはDMARCレコードである。v=DMARC1でバージョンを示し、p=rejectで、本人確認をクリアしなかったメールについて、その受信者が受信を拒否するように指定している。rua=mailto:postmaster@mtkn.jpでは、本人確認をクリアしなかったメールを受信した場合の通報先を指定している。</p> -<figure> -<img src="20250519-3.jpg" alt="フルートを吹くNoian"> -<figcaption>フルートを吹くNoian</figcaption> -</figure> +<h4>PTR</h4> +<p>PTRレコードを設定する。さくらのVPSの場合、VPS管理画面にログインし、ネットワーク>ホスト名逆引き登録と進み、カスタムホスト名に自分のドメイン名(mtkn.jp)を入力する。 +</p> +<h3>OpenSMTPDの設定</h3> +<h4>ユーザーの作成</h4> <p> -ルーマニアに竹は自生していない。ところが和食屋等には植わっているという。ルーマニアの植生だいじょうぶかなぁ。</p> +メールの送受信を担当するユーザーを作成する:</p> +<pre><code># useradd -m -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail +</code></pre> +<h4>サーバー証明書の取得</h4> <p> -山から家に帰ってきたらもう15時になろうとしていた。昼ごはんは食べてない。車で和歌山市の寿司屋に向った。弥一という回転寿司である。回転寿司だが安いわけではなく、味はかなり美味しい。</p> +<code>httpd(8)</code>の設定ファイルを作成する。<code>/etc/examples</code>からサンプルの設定ファイルをコピーして編集する。変更箇所はドメイン名だけでいい。編集後、httpdを起動する。</p> +<pre><code># cp /etc/examples/httpd.conf /etc/ +# vi /etc/httpd.conf +# echo httpd_flags= &gt;&gt; /etc/rc.conf.local +# rcctl start httpd +# rcctl enable httpd +</code></pre> <p> -中途半端な時間に行ったからか、寿司はひとつも回っておらず、タブレットから注文するように言われた。おなかがすいたから寿司以外のものも食べるといって、彼は最初にうどんを注文した。僕も自分の食べたいものを適当に注文していた。しばらくして納豆うずらが届いた。僕は頼んでいない。彼は納豆が好きだそうだ。ルーマニアにも臭いの強い発酵食品があるのかと思い、チーズの話をしてみた。ルーマニアには臭いチーズはないと言う。ゴルゴンゾーラやカマンベールはないのかと聞くと、あんなのは臭いうちにはいらないそうだ。ちなみに国でよく食べられるのはカビの生えていないクリームチーズのようなものだそうだ。</p> +続いて<code>acme-client(1)</code>を使ってLet's Encryptでサーバー証明書を発行する。まずはこちらもサンプルの設定ファイルをコピーしてドメイン名を変更する。その後、<code>acme-client(1)</code>を実行し、さらに<code>cron(8)</code>に登録することで、証明書が自動で更新されるようにする。</p> +<pre><code># cp /etc/examples/acme-client.conf /etc/ +# vi /etc/acme-client.conf +# acme-client -v mail.mtkn.jp +# crontab -e +</code></pre> -<p> -ひととおり食べておなかがいっぱいになってきた。彼もおなかがいっぱいになってきたと言いだした。彼のうどんにはまだ手が付けられていない。テーブルにはまだ寿司が残っている。無理なら僕が食べようかと言うと彼はうどんの鉢をこっちによこした。ちょっとくるしかったがまあうどんなので食べれた。麺は腰があっておいしいが、出汁は少し甘みが強かった。出汁を飲みほした頃、彼も残った寿司をだいたい片付けて、やっぱり自分もうどんを食べるといって追加で注文した。</p> +<pre><code>#minute hour mday month wday [flags] command +~ 2 * * * acme-client mail.mtkn.jp &amp;&amp; rcctl restart smtpd +</code></pre> +<h4>ドメインキーの作成とDNSへの登録</h4> <p> -うどんの甘みをガリで消して店を出た。おごってくれた。</p> +OpenSMTPD用のDKIMフィルターをインストールする。 +</p> +<pre><code># pkg_add opensmtpd-filter-dkimsign +</code></pre> <p> -弥一を後にし、和歌山城に案内した。和歌山城にはなぜか小さな動物園が付いている。時間がなかったので適当に見て天守閣に登った。地元の人間なのに天守閣はこれが初めてである。閉館ぎりぎりに行って30分しか見れなかった。天守閣は米軍に丸焼きにされたあと鉄筋コンクリート造りで再建された物である。外観はそれっぽいが内部は昭和の団地のような近代的なものだった。書画や武器等が展示されていてそれなりに見応えがあり、30分では足りなかった。彼は中国語が話せるので漢字も結構読める。僕が流し見した巻物を熱心に読んでいた。</p> +ドメインキーとしてRSAの秘密鍵と公開鍵を作成する。DNSのTXTレコードの制約により、鍵の長さは1024ビットにする。 +</p> +<pre><code># openssl genrsa -out /etc/mail/dkim/private.key 1024 +# openssl rsa -in /etc/mail/dkim/private.key -pubout -out /etc/mail/dkim/public.key +# chown -r _dkimsign:_dkimsign /etc/mail/dkim +# chmod 0400 /etc/mail/dkim/private.key +</code></pre> -<p> -和歌山城を出た後は寺か神社が見たいというので日前宮に行った。既に閉まっていて入れなかった。こんな時間に閉まっては会社帰りに立ち寄れないのではと言われた。ルーマニアではサラリーマンが帰りに教会に行くのが普通なのだろうか。</p> +<p>作成したドメインキーの公開鍵をDNSに登録する:</p> +<table> +<thead> +<tr> +<th>サブドメイン</th> +<th>種別</th> +<th>内容</th> +<th>優先度</th> +</tr> +</thead> +<tbody> +<tr> +<td>selector1._domainkey.mail</td> +<td>TXT</td> +<td>v=DKIM1;k=rsa;p=<i>公開鍵</i></td> +<td></td> +</tr> +</tbody> +</table> <p> -カフェにでも行って話しをしようということになった。いい喫茶店は知らないので和歌山市駅のスタバに行くことにした。駅ビルには確か日本酒のお店もあったのでそこで黒牛か超久でも買わせようと思った。ところが着いてみると平和酒造の店だった。この酒蔵はあまり好きではないので入らずに直接スタバに行った。スタバではどういう流れだったか、国境の話になった。昔は国という概念も国民という枠組みもなかったのに、いつしか作られて国境ができたという。ヨーロッパの世界はかつて神が治めていたが、あるときから貴族がその役割を担い、貴族が没落してからは民衆をまとめるために民族や国家というストーリーが必要になったという。そういえばゲルマン民族というのもナチスがドイツをまとめるために作った概念だとどこかで読んだ気がするが本当なのだろうか。日本は海に囲まれているのでヨーロッパと少し状況が違うんじゃないかという話を出してみた。言語も民族も一つで天然の国境があるのはヨーロッパ人から見るとちょっと変なのかな。そのくせ昔から文化的には国際色豊かだと言われた。</p> +サブドメインのselector1は以下のsmtpd.confで<code>filter-dkimsign</code>の-sオプションに +指定してものと同じものであれば他のものでもいい。</p> +<h4>smtpd.confの設定</h4> <p> -辺りがすっかり暗くなったころスタバを出て少しそのへんを散歩した。駅の周辺は全然土地勘がなかったが紀の川があるのは知っていたのでそっちに向かって適当に歩いた。日本語の練習をしたいので日本語で話して欲しいと言われた。僕の言葉は結構理解できていたようだし、向うも割と話せていた。多少ゆっくり話すようにはしたがそれでもたいしたものである。ところで外国人相手に母語を話すと普段どれだけ自分がいい加減に話しているかがよく分かる。発音や文法を間違えないように意識するととたんに言葉に詰まるのだ。</p> +/etc/mail/smtpd.confを以下の通り変更:</p> +<pre><code># 認証に使用するサーバー証明書と秘密鍵を&quot;mtkn.jp&quot;という名前で登録。 +# 証明書のファイルは先程acme-client(1)で取得したもの。 +pki mtkn.jp cert &quot;/etc/ssl/mail.mtkn.jp.fullchain.pem&quot; +pki mtkn.jp key &quot;/etc/ssl/private/mail.mtkn.jp.key&quot; -<p> -人生で英語をしっかり話したのはこれが初めてである。彼と話していて思ったが、話したいことがあれば文章を組みたてる前に話しはじめたほうがいい。相手の話を理解したうえでなにか言いたげなことは伝わるし、そのあとは身振り手振りでも案外なんとかなる。外国人と話すのに外国語に関する知識はある程度必要だが、相手の話したいことに興味を持つのも重要である。</p> +# テーブルの登録 +# ローカルに届いたメールの転送を設定するテーブル +table aliases file:/etc/mail/aliases +# ユーザーの名前と暗号化されたパスワードのテーブル +table passwd file:/etc/mail/passwd +# メールの転送を設定するテーブル +table virtuals file:/etc/mail/virtuals -<p> -河原では日本の山に危険な動物はいるのかという話になった。熊や毒蛇、スズメバチなんかが怖いと言った。このあたりで熊を見たことはないけど。それから彼がどうやって英語を流暢に話せるようになったのかも教えてくれた。英語でゲームをしていたら英語が第二の母語のようになったそうだ。ルーマニア語はマイナーな言語なのでゲームは翻訳されないらしい。僕も流暢ではないにしても何とか意思疎通できるようになったのは、英語でコンピューターの勉強をしたからだと思う。英語を勉強するより、英語を使ってなにかをすると、すぐ覚えるし忘れにくい。最後に二宮金次郎の像に集まった猫を眺めて駅に戻った。</p> +# フィルターの登録 +# ドメインキーによる署名を付加するフィルター +filter dkim_sign proc-exec\ &quot;filter-dkimsign -d mail.mtkn.jp -s selector1\ -k /etc/mail/dkim/private.key&quot; user _dkimsign group _dkimsign +# DNSの逆引き(PTRレコード)ができなかった場合に受信を拒否するフィルター +filter check_rdns phase connect match !rdns disconnect &quot;550 no rDNS.&quot; +# 正引き(Aレコード)と逆引き(PTRレコード)でDNSのレコードが一致しなかった +# 場合に受信を拒否するフィルター +filter check_fcrdns phase connect match !fcrdns disconnect &quot;550 no FCrDNS.&quot; -<p> -彼は明日の22時台の飛行機でイスタンブールを経由して国に帰る。イスタンブールまで12時間、乗り換えに3時間、そこからルーマニアまで1時間半、国に着いてからさらに12時間バスに乗る。なかなか遠いところである。国内線もあると言っていたが、日本でお金を使いすぎたから節約したいらしい。</p> +# 各ネットワークインターフェース毎の処理を設定。 +listen on socket +listen on lo0 +# vio0というインターフェースの25番ポートに届いたものに関する設定。 +# 外部からmtkn.jp宛に届いたものの処理である。 +# 最初に登録した証明書と秘密鍵を使ってMTAの本人確認をし、 +# 上で設定したDNSに関するフィルターを適応。 +listen on vio0 port 25 tls pki mtkn.jp hostname &quot;mail.mtkn.jp&quot;\ filter { check_rdns, check_fcrdns } +# vio0というインターフェースのsubmission(587番)ポートに関する設定。 +# mtkn.jpのユーザー(e.g. user@mtkn.jp)から任意の人宛に送信される +# メールに関する設定。 +# 上と同様に&quot;mtkn.jp”という名前で登録されたpkiを使ってMTAの本人確認をし、 +# &quot;passwd&quot;という名前で登録したパスワードで認証し、 +# さらにドメインキーでメールに署名する。 +listen on vio0 mask-src port submission smtps pki mtkn.jp\ hostname &quot;mail.mtkn.jp&quot; auth &lt;passwd&gt; filter dkim_sign -<p> -彼は20:30発の特急サザンに乗って大阪のホテルに帰っていった。</p> -]]></description> -</item> -<item> -<title>使用しているハードウェア、ソフトウェア</title> -<link>https://www.mtkn.jp/computer/what-i-use.html</link> -<guid>https://www.mtkn.jp/computer/what-i-use.html</guid> -<pubDate>Mon, 28 Apr 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>使用しているハードウェア、ソフトウェア</h1> -<time>2025-04-28</time>更新<br> -<time>2021-12-13</time>作成 -<h2>ノートパソコン</h2> -<p>メインのパソコン。</p> -<dl> -<dt>ハードウェア: <a href="openbsd_on_u9310e.html">Fujitsu LIFEBOOK U9310/E</a></dt> -<dd>2013年製のMacBook Airを使っていたがバッテリーがへたってきた。ぱちもんのバッテリーは1年でだめになるのに7千円くらいするのでコスパが悪い。2025年4月に大阪日本橋で見付けたLIFEBOOKを買った。10世代のCore i5で27000円くらい。軽い。ディスプレイが綺麗、OpenBSDと相性がそこそこいい。</dd> -<dt>OS: OpenBSD</dt> -<dd> -使いやすい。 -</dd> - -<dt>Window Manager: <a href="https://git.mtkn.jp/dwm">dwm</a></dt> - -<dt>Terminal: <a href="https://git.mtkn.jp/st">st</a></dt> - -<dt>Text Editor: acme, nvi</dt> -<dd>vimは辞めた。OpenBSDにあったnviが丁度いい。日本語を使うので日本語入力が必要ない場面ではacmeを使っている。</dd> - -<dt>Browser: firefox</dt> -<dd>もうちょっとましなのないんかな。UIころころ変わるし、重いし、嫌い。JavaScriptがないと困るので仕方なく使っているが...特に最近画面上部のUIが大きすぎて邪魔である。</dd> +# 各種メールの処理を定義する。 +# 各処理が実行される条件はこの後定義する。 +# ローカルメールはデフォルトのまま +action &quot;local_mail&quot; mbox alias &lt;aliases&gt; +# 自分のドメイン宛のメールは/var/vmail/以下に振り分けてmaildir形式で保存。 +action &quot;domain&quot; maildir &quot;/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}&quot;\ virtual &lt;virtuals&gt; +# 他のドメイン宛ては宛先へ転送。 +# 他のsmtpサーバーとの接続を確立する際にデフォルトではこのコンピュータのホスト名(僕の場合"sakura.mtkn.jp")で +# 自己紹介しており、DNSレコードが見付からないとおこられていたため helo "mtkn.jp" を追加 +action &quot;outbound&quot; relay helo "mtkn.jp" -<dt>Mail Client: neomutt</dt> -<dd>あんまり好きじゃない</dd> -</dl> +# どういう条件でどういう処理をするかを定義する。 +# 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。 +# ここでは/var/vmail/以下に振り分けて保存する。 +match from any for domain mtkn.jp action &quot;domain&quot; +# このサーバー内でのメールはlocal_mailとして処理。 +match from local for local action &quot;local_mail&quot; +# このサーバー内から他所へのメールは宛先に転送 +match from local for any action &quot;outbound&quot; +# 外部から来た他所宛てのメールは認証されているものに限り転送。 +# authを忘れると多分このサーバー経由で迷惑メールが送られる危険がある。 +match auth from any for any action &quot;outbound&quot; +</code></pre> -<h2>デスクトップ</h2> -<p>学生のときに組んだ。無駄にハイスペック。この上RTX2060も積んでたが、OpenBSDで使えないので解雇した。最近は絵を描くのに使っている。</p> -<dl> -<dt>マザーボード: ASRock Z390 Pro4</dt> -<dt>CPU: Intel Core i7-9700K</dt> -<dt>メモリ: センチュリーマイクロ 8GB * 2</dt> -<dd>国産</dd> -<dt>ストレージ: KimMiDi SSD 256GB</dt> -<dd>ThinkPad買ったときについてきたやつ。</dd> -<dt>モニタ: Eizo FlexScan EV2451</dt> -<dd>綺麗やけど質量が大きい。</dd> -<dt>OS: OpenBSD</dt> -<dd></dd> -</dl> +<h4>各テーブルの作成</h4> +<p> +上記の設定ファイルの<code>table</code>で指定したテーブルを作成する。<code>/etc/mail/aliases</code>は特に変更しなくてもいい。<code>/etc/mail/passwd</code>にはメールアカウントと暗号化されたパスワードを保存する。パスワードの暗号化には<code>smtpctl(8)</code>を使う:</p> +<pre><code># echo user@mtkn.jp:$(smtpctl encrypt) &gt;&gt; /etc/mail/passwd +<i>パスワードを入力し、エンター、Ctrl-D</i> +</code></pre> -<h2>家のサーバー</h2> <p> -主にDNSとして使っている。 +<code>/etc/mail/virtuals</code>にはメールの転送を設定できる。以下のようにする:</p> +<pre><code>abuse@mtkn.jp user@mtkn.jp +postmaster@mtkn.jp user@mtkn.jp +webmaster@mtkn.jp user@mtkn.jp +user@mtkn.jp vmail +</code></pre> +<p> +この設定で、例えばabuse@mtkn.jp宛てのメールがuser@mtkn.jpに転送される。abuse, postmaster, webmasterはなんか一応作っとけみたいに書いてたけど必要なんかな? </p> -<dl> -<dt>ハードウェア: Dell D520</dt> -<dd>おじいちゃんの家にころがってたのをもらってきた。</dd> -<dt>OS: OpenBSD</dt> -<dd></dd> -</dl> - -<h2>ウェブサーバー</h2> -<p>このウェブページ用</p> -<dl> -<dt>VPS: さくらのVPS</dt> -<dd>ちょっと高い(7078円/12ヶ月)。OpenBSDを入れられる。</dd> -<dt>OS: OpenBSD</dt> -<dt>httpサーバー: Openbsd httpd</dt> -<dd>manが分かりやすい。</dd> -<dt>smtpサーバー: OpenSMTPD</dt> -<dd>manが分かりやすい。</dd> -<dt>imapサーバー: dovecot</dt> -<dd>manはあんまり分かり易くない。<dd> -</dl> -]]></description> -</item> -<item> -<title>OpenBSD on Fujitsu LIFEBOOK U9310/E</title> -<link>https://www.mtkn.jp/computer/openbsd_on_u9310e.html</link> -<guid>https://www.mtkn.jp/computer/openbsd_on_u9310e.html</guid> -<pubDate>Mon, 28 Apr 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>OpenBSD on Fujitsu LIFEBOOK U9310/E</h1> -<time>2025-04-28</time> -<h2>はじめに</h2> +<h3>Dovecotの設定</h3> <p> -大阪の日本橋で状態のいいLIFEBOOKを見つけたので購入した。OpenBSDをインストールしたらおおむね問題なく動いた。</p> - -<h2>ハードウェア</h2> -<img src="images/openbsd_on_u9310e_1.jpg" alt="U9310/E"> +MDAとして<code>dovecot(1)</code>をインストールする。</p> +<pre><code># pkg_add dovecot +</code></pre> <p> -FujitsuのLIFEBOOK U9310/Eというパソコンである。2020年にビジネス向けに発売されていたもので、リース会社から放出されたものだと思う。CPUはIntelの第10世代のCore i5 10310U、メモリは8GB、ストレージはKIOXIA製NVMeの256GB。メモリは増設できないがWindowsなんてものは使わないので十分である。ディスプレイは多分IGZOのFull HDでめちゃ綺麗。キーボードは必要十分かなあ。ただし特定の組み合わせのキーを同時に入力することができない。A、W、Dが同時に入力できないのでゲームするには少し困りそう。またCapsLock、Shift、PgUpの組み合せもだめだった。CapsLockにはCtrlを割り当てており、この組み合わせでstの文字を拡大するようにしていたのでこれが使えないのは不便である。stの設定をいじって回避した。タッチパッドもまあまあ。左右クリック用に物理ボタンがあるのは凄くいい。でもThinkpadみたいにタッチパッドの上に付いてた方がホームポジションから動かなくていいので好き。ファンは結構静かだと思う。YouTubeを開くと少し回るがすぐに静かに戻る。今回のものはキーボードにバックライトがないモデルだった。少し不便だがすぐ慣れると思う。</p> +dhparamを作成する(時間がかかる): +</p> +<pre><code># openssl dhparam -out /etc/ssl/dh.pem 4096 +</code></pre> -<h2>内部</h2> <p> -分解は簡単。電源を落して、裏面にあるネジを全部外せば裏蓋が開く。ツメで引っ掛けていたりしないので外しやすい。裏蓋にはバッテリーの絵が描かれたボタンがあって、いかにも押してほしそうな顔をしていたので一応押してから分解した。組み立てた後は電源ケーブルを繋がないと電源が入らなかった。おそらくバッテリーとの接続を切断するためのボタンなのだと思う。</p> -<img src="images/openbsd_on_u9310e_2.jpg" alt="U9310/E の内部"> +わかりにくい設定ファイルをどけてシンプルなものに書き直す: +</p> +<pre><code># mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig +# vi /etc/dovecot/dovecot.conf +</code></pre> <p> -CPUクーラー、ヒートシンク、Wi-Fiカード、WWANカード、SSDは簡単に外せた。他のものもだいたい分解しやすそうである。電源プラグが交換できるのはポイントが高い。上でも書いたがメモリははんだ付けされていて交換できない。キーボードも外せそうではあるがマザーボードを取ったうえネジが沢山あったのでやめにした。</p> -<h2>OpenBSDのサポート</h2> -<table> -<tr> -<th>機能</th> -<th>サポート</th> -<th>備考</th> -</tr> +ドキュメントは公式ウェブサイトで確認できる。分量が多すぎて読む気にならない。サンプルの設定ファイルにも各項目の説明と既定値が書いてあるがこれも分かりにくい。パスワードが漏洩するのはとりあえず避けたいので、<code>ssl = required</code>と暗号化されていないIMAPを<code>port = 0</code>として無効にする。他はとりあえず以下の設定で動く:</p> +<pre><code># IPv4は全て監視 +listen = * -<tr> -<td>音</td> -<td>○</td> -<td> -Intel 400 Series HD Audio、コーデック: Realtek ALC255。<br>azaliaによりサポート。メディアキーによる音量調整が可能。</td> -</tr> +# SSL接続のみ許可 +ssl = required -<tr> -<td>バッテリーの状態</td> -<td>○</td> -<td> -acpibatによりサポート。 -</td> -</tr> +# SSLに使用するファイル +ssl_cert = &lt;/etc/ssl/mail.mtkn.jp.fullchain.pem +ssl_key = &lt;/etc/ssl/private/mail.mtkn.jp.key +ssl_dh = &lt;/etc/ssl/dh.pem -<tr> -<td>キーボードバックライト</td> -<td>?</td> -<td> -今回購入したものには搭載されていなかった。 -</td> -</tr> +# メールの保存場所は各ユーザーのホームディレクトリ。 +# ホームディレクトリの場所は以下のuserdbで設定。 +mail_location = maildir:~ -<tr> -<td>ハイバーネーション</td> -<td>○</td> -<td> -ZZZが使える。 -</td> -</tr> +# パスワードのファイル。 +# OpenSMTPDと共有。 +passdb { + args = scheme=BLF-CRYPT /etc/mail/passwd + driver = passwd-file +} -<tr> -<td>スリープ</td> -<td>○</td> -<td> -zzzが使える。液晶を閉じると自動でスリープし、開くと問題なく復帰できる。電源ランプが点滅するのが少しじゃま。 -</td> -</tr> +# ユーザーの設定。 +# ホームディレクトリは/var/vmail/<i>ドメイン</i>/<i>ユーザー名</i> +userdb { + args = uid=vmail gid=vmail home=/var/vmail/%d/%n + driver = static +} -<tr> -<td>タッチパッド</td> -<td>○</td> -<td> -imtでサポート。 -</td> -</tr> +# 使用するプロトコル。 +protocols = imap lmtp -<tr> -<td>USB type-A</td> -<td>○</td> -<td> -使えた。 -</td> -</tr> +# SSLを使ったIMAPSのみを許可。 +service imap-login { + inet_listener imaps { + port = 993 + ssl = yes + } + # Disable imap + inet_listener imap { + port = 0 + } +} +</code></pre> +<pre><code># rcctl start dovecot +# rcctl enable dovecot +</code></pre> -<tr> -<td>USB type-C、Thunderbolt</td> -<td>?</td> -<td> -充電はできた。Thunderboltはケーブルがないのでテストできない。</td> -</tr> +<p> +サーバーの証明書が更新される際に<code>dovecot(1)</code>も再読み込みされるように<code>cron(8)</code>に追加しておく: +</p> +<pre><code>crontab -e +</code></pre> -<tr> -<td>SDカードリーダー</td> -<td>○</td> -<td> -使えた。 -</td> -</tr> +<pre><code>#minute hour mday month wday [flags] command +~ 2 * * * acme-client mail.mtkn.jp &amp;&amp; rcctl restart smtpd &amp;&amp; rcctl reload dovecot +</code></pre> -<tr> -<td>映像出力</td> -<td>○</td> -<td> -inteldrmでサポート。輝度の調整はxbacklightで出来るが、メディアキーは使えなかった。HDMIの出力も問題ない。 -</td> -</tr> - -<tr> -<td>ウェブカメラ</td> -<td>○</td> -<td> -SunplusIT Inc FJ Camera。uvideoでサポート。 -</td> -</tr> - -<tr> -<td>Ethernet</td> -<td>○</td> -<td> -emでサポート。 -</td> -</tr> - -<tr> -<td>Wi-Fi</td> -<td>○</td> -<td> -Intel Wi-Fi 6 AX201。iwxでサポート。 -</td> -</tr> - -<tr> -<td>WWAN</td> -<td>?</td> -<td> -Sierra Wireless, Incorporated EM7430。umbにより認識はされてる。ifconfigでもネットワークインターフェースとして表示されてる。simカード持ってないから試せない。 -</td> -</tr> - -<tr> -<td>Bluetooth</td> -<td>×</td> -<td> -OpenBSDはBluetoothが嫌い。ugenにより認識はされてる。 -</td> -</tr> +<h3>テスト</h3> +<p> +手元のメールクライアントから送受信のテストをする。特にgmail等に送った際に迷惑メールに分類されないかどうか確認する。以上の設定で、gmailへの送受信は問題なかった。また、<code>neomutt(1)</code>、<code>thunderbird(1)</code>からログイン及び送受信できることも確認した。</p> -</table> +<h2>参考文献</h2> +<ul> +<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-1-what-is-email.html">Arch Linux mail server tutorial - part 1 - What is email?.unixsheikh.com</a></li> +<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-2-opensmtpd-dovecot-dkimproxy-and-lets-encrypt.html">Arch Linux mail server tutorial - part 2 - OpenSMTPD, Dovecot, DKIMproxy, and Let's Encrypt.unixsheikh.com</a></li> +<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-3-get-dns-right-it-is-important.html">Arch Linux mail server tutorial - part 3 - Get DNS right, it's important!.unixsheikh.com</a></li> +<li><a href="https://man.openbsd.org/smtpd">smtpd(8).OpenBSD Manual Pages</a></li> +<li><a href="https://man.openbsd.org/smtpd.conf">smtpd.conf(5).OpenBSD Manual Pages</a></li> +<li><a href="https://man.openbsd.org/httpd">httpd(8).OpenBSD Manual Pages</a></li> +<li><a href="https://man.openbsd.org/httpd.conf">httpd.conf(5).OpenBSD Manual Pages</a></li> +<li><a href="https://man.openbsd.org/acme-client">acme-client(1).OpenBSD Manual Pages</a></li> +<li><a href="https://man.openbsd.org/acme-client.conf">acme-client.conf(5).OpenBSD Manual Pages</a></li> +<li><a href="https://doc.dovecot.org/">Dovecot manual — Dovecot documentation</a></li> +</ul> ]]></description> </item> <item> -<title>大阪万博行った</title> -<link>https://www.mtkn.jp/journal/posts/20250420.html</link> -<guid>https://www.mtkn.jp/journal/posts/20250420.html</guid> -<pubDate>Wed, 23 Apr 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>大阪万博行った</h1> -<time>2025-04-20</time> - -<p>4月15日火曜日に大阪万博に行ってきた。 +<title>RP2040 SDKなし3 割り込み</title> +<link>https://www.mtkn.jp/computer/rp2040_3_interrupt.html</link> +<guid>https://www.mtkn.jp/computer/rp2040_3_interrupt.html</guid> +<pubDate>Wed, 27 Aug 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>RP2040 SDKなし3 割り込み</h1> +<time>2025-08-27</time><br /> +<p> +前回UARTで通信する際、FIFOにデータが届いていないかを確認しつづけていた(ポーリング)。 +これでは電力がもったいないので、データが届くまでCPUは休ませておいて、 +届いたときだけ処理を実行するように変更する(割り込み)。 +<p> +<p> +前回: <a href="rp2040_2.html">RP2040 SDKなし2 Clock、UART</a><br> +ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex3_interrupt </p> -<p>朝の紀州路快速は高校生ばかりだった。大阪に近付くにつれてビジネスマンが多くなっていった。西九条から地下鉄に乗りかえるとEXPO staffの札を首から下げた外国人がたくさんいた。外国人ばかりなのになぜかみんなスマホを見たり、目が合ってもなんとなく外を向いたりと、雰囲気だけが日本なのが面白い。</p> +<h2>動作環境</h2> +<ul> +<li>OpenBSD 7.7 + <ul> + <li>arm-none-eabi-binutils-2.40</li> + <li>OpenBSD Make</li> + <li>minicom version 2.8</li> + </ul> +</li> +<li><a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a> +</li> +</ul> +<h2>割り込み</h2> <p> -会場には予約より少し早く着いた。荷物検査があるので結構時間がかかり、入れたのは予約より30分ほど過ぎたころだった。</p> -<figure> -<img src="20250420-1.jpg" alt="東ゲート"> -<figcaption>東ゲート</figcaption> -</figure> -<figure> -<img src="20250420-2.jpg" alt="リング"> -<figcaption>リング</figcaption> -</figure> +ハードウェアが外部から信号を受けとると、CPUに現在の処理を中断させてその信号を処理させるようにできる。これを割り込み処理という。今回はUARTにデータが届いた時に同じデータをUARTに書き込むようにしたい。</p> -<p>会場内はところどころ植え込みがあって雑草も生えてきていた。タンポポは全部セイヨウだった。中央付近に静けさの森という場所があるが、そんなに森にはなっていない。どうせなら明治神宮みたいなのを作ればよかったかと思う。IR会場になった後も公園にできるし。 -</p> -<figure> -<img src="20250420-3.jpg" alt="セイヨウタンポポ"> -<figcaption>セイヨウタンポポ</figcaption> -</figure> -<figure> -<img src="20250420-4.jpg" alt="ノゲシ"> -<figcaption>ノゲシ</figcaption> -</figure> -<figure> -<img src="20250420-5.jpg" alt="バラ科のなにか"> -<figcaption>バラ科のなにか</figcaption> -</figure> -<figure> -<img src="20250420-6.jpg" alt="ノボロギク"> -<figcaption>ノボロギク</figcaption> -</figure> -<figure> -<img src="20250420-7.jpg" alt="ノアザミ"> -<figcaption>ノアザミ</figcaption> -</figure> -<figure> -<img src="20250420-8.jpg" alt="タネツケバナ"> -<figcaption>タネツケバナ</figcaption> -</figure> -<figure> -<img src="20250420-9.jpg" alt="ヨモギ"> -<figcaption>ヨモギ</figcaption> -</figure> +<h3>割り込みを有効化</h3> +<p> +割り込みの信号を受け取るために、割り込みを有効化する。CPUが割り込みを受け付けるようにする設定と、UARTが割り込み信号を発生させるようにする設定が必要である。</p> +<p> +CPU側の設定は<code>M0PLUS: NVIC_ISER</code>レジスタの有効化したい割り込みのビットに1を書くことで行う。有効化できる割り込みは全部で26個ある。割り込みの種類はrp2040のデータシート[1]のTable 80. Interruptsに書いてある。今回有効化するのはUART0の割り込みで、この表から20番が割り当てられていることが分かるので、<code>M0PLUS: NVIC_ISER</code>に、下から20番目のビットを1にしたもの書き込む:</p> +<code><pre> // enable uart interrupt in cpu + ldr r4, ppb_base + mov r5, #0xe1 + lsl r5, #8 + mov r0, #1 + lsl r0, #20 + str r0, [r4, r5] // M0PLUS: NVIC_ISER -<p>11時に英国パビリオンを予約していたのだが着いてみるとシステムエラーで閉鎖されていた。レストランは開いていたので混む前になにか食べることにした。英国人と会話できると思っていたが案内してくれたのは片言の英語を喋るアジア人のおばちゃんだった。2個で9ポンドのアイスクリームは売ってなかった。フィッシュアンドチップスと紅茶を頼んだ。出された紅茶にはミルクが付いていなかった?!フィッシュアンドチップスの方は思っていたよりは美味しかった。味のしない鱈だったが鮮度は良く、衣もさくさくだった。油もそこまで古くなっていない。チップスの方はモスバーガーよりイモの味が薄かった。タルタルソースと緑のペーストが付いていた。緑のものは多分エンドウ豆を潰したものにミントで香りを付けていた。悪くはなかったが組合せが初めてだったせいか、ミントが目立ちすぎていたように思う。</p> -<figure> -<img src="20250420-10.jpg" alt="フィッシュアンドチップス"> -<figcaption>フィッシュアンドチップス</figcaption> -</figure> +/* ... */ -<p>英国パビリオンが復旧するまで他を周ることにした。オーストリアに行ってみたが列が一杯だから時間をおいて来るように言われた。</p> +ppb_base: + .word 0xe0000000 +</pre></code> +<p> +UART側の設定は、<code>UART: UARTIMSC</code>レジスタで行なう。データを受け取ったときに割り込みを発生させたいので、<code>RXIM</code>ビットに1を書き込む。また、処理を開始する前に既にFIFOにデータがたまっている可能性があるので、FIFOが空ではないときに割り込みを発生させるように、<code>RTIM</code>にも1を書き込む:</p> +<code><pre> // enable uart interrupt in uart0 subsystem + ldr r1, uart0_base + mov r2, #(1 &lt;&lt; 6 | 1 &lt;&lt; 4) // RTIM | RXIM + str r2, [r1, #0x38] // UART: UARTIMSC +</pre></code> +<p> +また、FIFOにデータがきたらすぐに割り込みを発生させたいので、<code>UART: UARTIFLS</code>レジスタの<code>RXIFLSEL</code>に0を書き込む。これでFIFOの1/8が埋まったら割り込みが発生する。</p> +<code><pre> // set fifo level to 0 + mov r2, #0 + str r2, [r1, #0x34] // UART: UARTIFLS +</pre></code> +<p> +これだとFIFOに届いた瞬間には割り込みがおこらない気がするが、pico-sdk(src/rp2_common/hardware_uart/include/hardware/uart.h)の<code>uart_set_irqs_enabled</code>関数でもこうなってる。FIFOを無効にすれば即座に割り込みがおこるかもしれないが、データが失われる可能性もあるのかな。</p> -<p>大根が咲いていた。 -</p> -<figure> -<img src="20250420-11.jpg" alt="ハマダイコン"> -<figcaption>ハマダイコン</figcaption> -</figure> +<h3>ベクターテーブルの設定</h3> +<p> +外部からの信号で割り込みが発生したときにCPUに行わせる処理はベクターテーブルにあらかじめ登録しておく必要がある。ベクターテーブルの場所は<a href="rp2040_1.html">一回目</a>に登録した通り<code>vectors</code>とラベルを付けた場所(main.sの先頭)である。ARMの仕様書[2]によると、ベクターテーブルの0番目は初期のスタックポインタで、そこから15個はARMによって使用用途が定められている。16番以降の32個は各CPUの設計者が自由に決められる。rp2040の割り込みについてはデータシート[1]のTable 80. Interruptsに一覧表が載っている。今回使いたいのはUART0の割り込みなので16 + 20 = 36番目に、割り込み時に呼ばれたい関数のポインタ(Thumb Modeなので関数のアドレスに1を足したもの)を書き込めばいい。それ以外のものは今回は使わないので適当な数値でうめておく:</p> +<code><pre> .global vectors +vectors: + .word 0x20040000 // initial SP + .word (reset+1) // entry point + .word 0xdeadbeef + .word 0xdeadbeef -<p>二つほど空いてる所を見たがあんまり面白くなかった。</p> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p>英国に戻ってみると復旧していたので入ることにした。前に英国人の親子が並んでいた。母親が高校生くらいの息子にハグして日本語で「トテモカワイイデス」と言っていた。突然日本語が聞こえてくるとどうしても見てしまう。目が合った。その後もなぜかちらほら日本語が聞こえてきては目が合った。しばらくして父親の方が息子の鞄からチョコ棒を出して母親と息子に配った。自分の分を取ったあとなぜかさらにもう一本取り出してこっちに差し出してきた。話し掛けたいなあと思っていた所だったので受け取ってお礼を言って何を話そうかと考えていたのだが、パビリオンの扉が開いて3人は吸い込まれていった。パビリオンは20人程度のグループで周るようになっていたのだが、丁度僕の前で区切られてしまったのだ。</p> -<figure> -<img src="20250420-12.jpg" alt="英国人がくれたチョコ棒"> -<figcaption>英国人がくれたチョコ棒</figcaption> -</figure> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p>展示は特に面白くなかった。特定の医薬品会社がお金を出していそうなことは分った。 -</p> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p>パビリオンの中にバーがあった。ビールでも飲みたかったが外が寒すぎて冷えていたのであきらめた。 -</p> -<p>その後は特に予定もなかったので散歩していたらルーマニアパビリオンを見つけた。チェロの先生がルーマニアで修行してきた人なので行ってみたかったのだが、予約のフォームで見つからなかったので来ていないものだと思っていた。予約できないだけみたいだ。</p> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p>30分ほど並んだ。入口に居たお姉さんがめっちゃ綺麗だった。展示は生演奏と伝統工芸の展示、実演だった。演奏は僕が行ったときはバイオリンだけだったが、椅子と譜面台が4つとピアノもあったので時間によって編成が変わるようだった。伝統工芸の展示のひとつに、ガラスに描かれた宗教画があった。ビザンツからの伝統だと言っていたと思う。絵の具が置いてあったので描かせてもらった。マリアがキリストを抱いている画を模写した。描きながらルーマニアのことを色々教えてもらえたのがとても良かった。女の人が全員綺麗だった。さすが東欧である。レストランもあったが残念ながら閉まっていて食べられなかった。</p> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p>最後に知りあったルーマニア人とその辺をぶらぶらして帰ってきた。</p> -<figure> -<img src="20250420-13.jpg" alt="知り合ったルーマニア人"> -<figcaption>知り合ったルーマニア人</figcaption> -</figure> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p>パビリオンの展示は総じていまいちである。今回行った海外のパビリオンには特に未来っぽいものはなにもなかった。これはある意味期待通りだった。一方でルーマニアでは現地の文化も見れたし現地人との交流もできたのでよかった。他のパビリオンでもこういう交流があればいいのにと思う。国内企業のパビリオンに行けていないので次回はその辺を見たい。</p> -]]></description> -</item> -<item> -<title>着物作った</title> -<link>https://www.mtkn.jp/journal/posts/20250221.html</link> -<guid>https://www.mtkn.jp/journal/posts/20250221.html</guid> -<pubDate>Fri, 21 Feb 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>着物作った</h1> -<time>2025-02-21</time> -<p> -普段着回している着物がへたってきたので新しいのを仕立てた。楽天で見付けた真綿紬の草木染めである。中古で2640円だった。新品だとおそらく何十万のしろものである。女物だったので解いて洗濯し、男物に仕立て直した。防虫剤の臭いがひどかったが、洗濯すればおおむね取れた。</p> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p> -解いた布を重ねてネットに入れ、普通に洗濯する。脱水が済んだら濡れた状態でアイロンをかけてしわをとり陰干しする。絹ものだがこれで特に問題なく洗濯できた。</p> + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef + .word 0xdeadbeef -<p> -裏地も一緒に洗濯した。これも多分羽二重の高価なものである。ただ袷にするのは面倒なので一重にした。袖付けがなかなか上手くいかずに人形のところが皺になる。居敷当ては箪笥に眠っていた唐草模様の風呂敷を使った。</p> + .word (uart_interrupt_handler+1) // UART0_IRQ +</pre></code> -<figure> -<img src="20250221.jpg" alt="作った着物"> -</figure> +<h3>割り込み処理</h3> +<p> +割り込み発生時に呼ばれる関数はUARTのFIFOに届いたデータをそのまま送信するだけのものである:</p> +<code><pre> // uart_interrupt_handler echos the data arrived at uart0 +uart_interrupt_handler: + push {lr} + bl getbyte + bl putbyte + pop {pc} +</pre></code> +<p> +<code>getbyte</code>、<code>putbyte</code>は前回と同じもの。 +</code> +<h2>メインループ</h2> <p> -真綿紬は初めてだが、軽くて暖かい。ほどかずに洗濯してよれよれにならなければいいのだが...</p> -]]></description> -</item> -<item> -<title>きのかわ弦楽合奏団 さくらコンサート</title> -<link>https://www.mtkn.jp/journal/posts/20250216.html</link> -<guid>https://www.mtkn.jp/journal/posts/20250216.html</guid> -<pubDate>Sun, 16 Feb 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>きのかわ弦楽合奏団 さくらコンサート</h1> -<time>2025-02-16</time> -<p>三月にコンサートあります。</p> -<figure> -<img src="20250216-1.jpg" alt="きのかわ弦楽合奏団のさくらコンサートのちらしの表面。令和7年3月23日14:00から、旧和歌山県議会議事堂にて"> -<img src="20250216-2.jpg" alt="きのかわ弦楽合奏団のさくらコンサートのちらしの裏面。楽団と会場の紹介"> -<figcaption>Copyright: きのかわ弦楽合奏団</figucaption> -</figure> +UARTにデータが届いていないときは特にすることがないのでCPUは寝かせておく。ARMには割り込みが発生するまでなにもしない<code>wfe</code>という命令があるのでこれを使う:</p> +<code><pre>loop: + wfe + b loop +</pre></code> + +<h2>完成</h2> +<p> +これで完成。動作は前回と全く同じだが、消費電力をおさえられる。</p> +<h2>参考</h2> +<ul> +<li> +[1]. <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> +</li> +<li> +[2]. <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> +</li> +<li> +[3]. <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> +</li> +</ul> ]]></description> </item> <item> -<title>謹賀新年</title> -<link>https://www.mtkn.jp/gallery/20250101.html</link> -<guid>https://www.mtkn.jp/gallery/20250101.html</guid> -<pubDate>Wed, 1 Jan 2025 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>謹賀新年</h1> -<time>2025-01-01</time> -<img src="img/20250101.jpg"> +<title>自画像</title> +<link>https://www.mtkn.jp/gallery/20250717.html</link> +<guid>https://www.mtkn.jp/gallery/20250717.html</guid> +<pubDate>Thu, 17 Jul 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>自画像</h1> +<time>2025-07-17</time> +<img src="img/20250717.jpg"> ]]></description> </item> <item> -<title>麻婆豆腐</title> -<link>https://www.mtkn.jp/kitchen/recipe/mapo_tofu.html</link> -<guid>https://www.mtkn.jp/kitchen/recipe/mapo_tofu.html</guid> -<pubDate>Thu, 10 Oct 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>麻婆豆腐</h1> -<time>2024-10-10</time>作成 -<figure> - <img src="../pics/mapo_tofu.jpg" alt="麻婆豆腐の画像"> - <figcaption>麻婆豆腐</figcaption> -</figure> -<div class="recipe-ingredient"> -<h2>材料(4~5人分)</h2> -<ul> - <li>豆腐: 2丁</li> - <li>ひき肉: 300g</li> - <li>長ネギ: 1本</li> - <li>にんにく: 適量</li> - <li>生姜: 適量</li> - <li>八角: 適量</li> - <li>花椒: 好きなだけ</li> - <li>豆板醤: 好きなだけ</li> - <li>味噌: 適量</li> - <li>酒: 適量</li> - <li>濃口醤油: 適量</li> - <li>塩: 少々</li> - <li>炒め油: 多め</li> -</ul> -</div> +<title>万博で知り合ったルーマニア人とハイキングに行った</title> +<link>https://www.mtkn.jp/journal/posts/20250519.html</link> +<guid>https://www.mtkn.jp/journal/posts/20250519.html</guid> +<pubDate>Mon, 19 May 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>万博で知り合ったルーマニア人とハイキングに行った</h1> +<time>2025-05-19</time> -<div class="recipe-steps"> -<h2>手順</h2> -<ol> - <li>豆腐を水切りしてサイコロに切る</li> - <li>長ネギ、にんにく、生姜を刻む</li> - <li>フライパンに油をしき八角、にんにく、生姜を入れ弱火で香りを出す</li> - <li>火を強めてひき肉を入れ、塩を少しふる</li> - <li>ひき肉に色がついたらネギを入れて炒める</li> - <li>酒、濃口醤油、味噌、豆板醤で味をつける</li> - <li>水を入れてひと煮立ちさせる</li> - <li>花椒、豆腐を入れる</li> - <li>好みで、ラー油、唐辛子、山椒を好きなだけ入れる</li> -</ol> -</div> +<p> +先日万博のルーマニアパビリオンで知り合ったルーマニア人の<a href="https://noian.art">Noian</a>がハイキングに行きたいというので、地元の山に連れていった。元々4月の終わりごろを予定していたが、僕が風邪をひいてしまいのびていた。向こうも万博の仕事が忙しいようで、帰国の前日になってやっと予定が合った。</p> -<h2>ひとこと</h2> -<p>我流の適当なレシピなので本場のものが良ければ陳建一さんのでも調べてほしい。</p> -<p>酒はできれば紹興酒のほうがいいと思う。味噌と一緒に豆鼓も入れていいかも。一般的にはもっと旨味を出すために中華だしやら味の素やらを入れると思うが、シンプルに辛いほうが好きなのでそういうものは入れない。陳さんは生姜を入れていなかったが僕はあっていいと思う。豆腐は塩水で茹でていたがこれは真似してもよさそう。</p> -]]></description> -</item> -<item> -<title>棚作った</title> -<link>https://www.mtkn.jp/journal/posts/20241009.html</link> -<guid>https://www.mtkn.jp/journal/posts/20241009.html</guid> -<pubDate>Wed, 9 Oct 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>棚作った</h1> -<time></time> +<p> +10時過ぎにうちの最寄り駅に着く予定だったが、電車に乗り間違えたらしく一時間程遅れた。なにせ阪和線である。外国人にはかなりややこしい。そのうえ駅員はフランス人のごとく英語を話さない。間違って関空に行かないようにしつこく注意喚起して、なんとか和歌山に辿りついた。</p> <p> -自室のクローゼットが使いにくかったので引っ剥がして新しいのを作った。収納していたのはほとんど本だったのだが、奥行きが深いのに棚板が少ないので前後に並べて置いていたため、どこになんの本があるのか見にくく、取り出しにくかった。あとパソコン置き場としても使っていたが、24インチのモニターが一枚ギリギリ入る程度の幅しかなかったのでこれも拡張したかった。久しぶりに木工もしたかったので木材を買って自作した。杉を基本に、パソコンを置く場所だけ檜にした。<p> +車で自宅まで連れて来て、そこから徒歩で山に向かった。自然が好きそうだったので、ろくに整備されていないひとけのない山に案内した。</p> -<figure> -<img src="20241009-1.jpg" alt="クローゼット跡地と買った木材"> -<figcaption>クローゼット跡地と買った木材</figcaption> -</figure> +<p> +この山に登るのは久しぶりなので道を覚えているか不安だった。おそらく昔の人が隣の町まで行くのに使っていた道だと思う。今でも一応隣町まで抜けられる。入口は果樹園になっていて今も柑橘の木がたくさん植わっている。川沿いを森に入ると左右には棚田が並んでいる。こちらの田んぼは既に放棄され、今は藪や針葉樹林になっている。針葉樹の方は戦後の拡大造林で植えられたものだろうか、毎年ティッシュペーパーの需要を底上げしている木々である。棚田は石垣で造成されている。いつのものかは知らないが今も崩れずに残っている。</p> + +<p> +棚田の横をさらに進むと小さい滝がある。牝牛の滝というらしい。牝牛と書いてコッテと読む。滝の近くに牝牛に見える岩があるのが由来らしい。コッテという言葉は聞き馴染みがない。後日祖父の友人に聞いた話だと、小さい牛やメスの牛のことをコッテウシと言ったそうだ。</p> <figure> -<img src="20241009-2.jpg" alt="パソコン机の部分"> -<figcaption>パソコン机の部分</figcaption> +<img src="20250519-1.jpg" alt="牝牛の滝"> +<figcaption><ruby>牝牛<rp>(</rp><rt>コッテ</rt><rp>)</rp></ruby>の滝</figcaption> </figure> <p> -パソコン机の天板は白木のままでは寂しいので亜麻仁油を塗ってオイルフィニッシュにしてみた。結構黄色が濃くなった。</p> +滝を過ぎると急勾配の斜面が続く。ほとんど道とはいえないような山肌を登る。40度はありそうな勾配である。息があがりながらも、彼は日本に来たがっている友人のことを聞かせてくれた。日本のとあるバンドの大ファンなのだそうだ。ポップカルチャーにはうといので自国のことなのになにも分からなかった。最後に大きな岩が作った崖を山羊のように登ればやっと平坦な道に出る。彼のブーツがよく滑っていた。裸足が一番安全だと勧めたが遠慮しとくと言われた。そこから先は特にきつい場所もなく、そのまま展望台に続く。彼は少し疲れたようで言葉が少なくなっていた。</p> <figure> -<img src="20241009-3.jpg" alt="机のオイルフィニッシュ"> -<figcaption>机のオイルフィニッシュ。下においているのが白木のもの</figcaption> +<img src="20250519-2.jpg" alt="展望台から"> +<figcaption>展望台から</figcaption> </figure> <p> -せっかくやるならと思って蟻を切ってみた。適当でも案外ガッチリはまるものである。英語ではdovetailというそうだ。鳩のしっぽという意味である。日本語の蟻よりも鳩のしっぽのほうがしっくりくる気がする。</p> +展望台からは僕の町が一望できる。西が海で南北が山、東はゆるがかな坂が高野山まで続いている。彼の出身地も山がちで似た地形だそうだ。海はないが毎年父親が黒海まで連れていってくれていたので馴染深いといっていた。展望台には小さな祠がある。せっかくなので鳥居のくぐり方や参拝の仕方を教えてあげた。熱心に聞いてはくれたが自分はやらないと言っていた。理由は聞かなかったがおそらく彼がキリスト教徒だからであろう。こういうはっきりした態度は見ていてなんとなく気持がいい。</p> -<figure> -<img src="20241009-4.jpg" alt="蟻"> -<figcaption>蟻</figcaption> -</figure> +<p> +ところで日本では神道でも仏教でも左進右退が基本だと思う。以前五瀬命が祀られている竃山神社のお祭に行ったことがある。そこでは巫女さんが神殿に向う階段を登る際左足を一段上げて右足を引き寄せてを繰り返して登り、下るときは逆にしていた。お寺でも道場に入るときは左から、出るときは右からと習った。そういえば合気道でも座礼をするとき左手、右手の順で床に付けて頭を下げ、頭を上げるときは右手から戻すと習った気がする(この記憶はあいまい)。ところがヨーロッパでは逆らしい。左は邪悪だから右から入るのだそうだ。</p> <p> -上に本棚をつけて完成。本棚は棚を移動しやすいようにと思い、ネジ止め式の棚受けを買った。めんどくさくなったわけではない。左下においているのはもとのクローゼットにあった引き出しである。</p> +下りは別の道を選んだ。途中からコンクリートで舗装されている楽な道である。途中で山菜がいろいろ生えていたので紹介してあげたらぱくぱく食べていた。よく信用できるものである。イタドリ、ユキノシタ、フキ、チャノキ、タラノキ...。タケノコはもう竹だった。クスノキもあったのでいい香りがすると言って渡したらこれもかじっていた。クスノキが食べられるかどうか僕は知らない。防虫剤に使われていたと伝えるとすぐに吐きだして水で口をすすいでた。</p> + +<p> +展望台でフルートを吹く動画を撮りたかったのに忘れていたといっていたが、途中で見つけた竹林を気に入ったようで、そこで吹いていた。このフルートはチューニングが現代の一般的なものとは違い、ミの音が少し低くなっている。長調とも短調ともいえない微妙な音階だった。</p> <figure> -<img src="20241009-5.jpg" alt="完成"> -<figcaption>完成</figcaption> +<img src="20250519-3.jpg" alt="フルートを吹くNoian"> +<figcaption>フルートを吹くNoian</figcaption> </figure> <p> -これを作るにあたり、大阪日本橋の刃物屋で鉋を買ってきた。これまではホームセンターで1000円ほどのものだったが、今回買ったのは8000円の物である。店主が台の仕込みと研ぎをした上で売ってくれた。きれいに切れるので小口面の直角を出すのが楽しくなった。小口を下にして自立するようになったときは結構気持ちいい。専門店での買い物は楽しい。餅は餅屋である。</p> +ルーマニアに竹は自生していない。ところが和食屋等には植わっているという。ルーマニアの植生だいじょうぶかなぁ。</p> -]]></description> -</item> -<item> -<title>さくらクレヨン</title> -<link>https://www.mtkn.jp/gallery/20240727.html</link> -<guid>https://www.mtkn.jp/gallery/20240727.html</guid> -<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>さくらクレヨン</h1> -<time>2024-07-27</time> -<img src="img/20240727.jpg"> -]]></description> -</item> -<item> -<title>さくらクレパス</title> -<link>https://www.mtkn.jp/gallery/20240702.html</link> -<guid>https://www.mtkn.jp/gallery/20240702.html</guid> -<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>さくらクレパス</h1> -<time>2024-07-02</time> -<img src="img/20240702.jpg"> -]]></description> -</item> -<item> -<title>さくらクレパス</title> -<link>https://www.mtkn.jp/gallery/20240629.html</link> -<guid>https://www.mtkn.jp/gallery/20240629.html</guid> -<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>さくらクレパス</h1> -<time>2024-06-29</time> -<img src="img/20240629.jpg"> -]]></description> -</item> -<item> -<title>さくらクレパス</title> -<link>https://www.mtkn.jp/gallery/20240628.html</link> -<guid>https://www.mtkn.jp/gallery/20240628.html</guid> -<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>さくらクレパス</h1> -<time>2024-06-28</time> -<img src="img/20240628.jpg"> -]]></description> -</item> -<item> -<title>きのかわ弦楽合奏団第7回定期演奏会</title> -<link>https://www.mtkn.jp/journal/posts/20240428.html</link> -<guid>https://www.mtkn.jp/journal/posts/20240428.html</guid> -<pubDate>Sun, 28 Apr 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>きのかわ弦楽合奏団第7回定期演奏会</h1> -<time>2024-04-28</time> +<p> +山から家に帰ってきたらもう15時になろうとしていた。昼ごはんは食べてない。車で和歌山市の寿司屋に向った。弥一という回転寿司である。回転寿司だが安いわけではなく、味はかなり美味しい。</p> -<p>先日きのかわ弦楽合奏団というアマチュアの団体に入団しました。その定期演奏会が7月にあります。多分出演します。</p> +<p> +中途半端な時間に行ったからか、寿司はひとつも回っておらず、タブレットから注文するように言われた。おなかがすいたから寿司以外のものも食べるといって、彼は最初にうどんを注文した。僕も自分の食べたいものを適当に注文していた。しばらくして納豆うずらが届いた。僕は頼んでいない。彼は納豆が好きだそうだ。ルーマニアにも臭いの強い発酵食品があるのかと思い、チーズの話をしてみた。ルーマニアには臭いチーズはないと言う。ゴルゴンゾーラやカマンベールはないのかと聞くと、あんなのは臭いうちにはいらないそうだ。ちなみに国でよく食べられるのはカビの生えていないクリームチーズのようなものだそうだ。</p> -<figure> -<img src="7th_concert.jpeg" alt="きのかわ弦楽合奏団の第七回定期演奏会のポスター。7月14日14:00から、かつらぎ総合文化会館、あじさいホールにて"> -<figcaption>Copyright: きのかわ弦楽合奏団</figcaption> -</figure> +<p> +ひととおり食べておなかがいっぱいになってきた。彼もおなかがいっぱいになってきたと言いだした。彼のうどんにはまだ手が付けられていない。テーブルにはまだ寿司が残っている。無理なら僕が食べようかと言うと彼はうどんの鉢をこっちによこした。ちょっとくるしかったがまあうどんなので食べれた。麺は腰があっておいしいが、出汁は少し甘みが強かった。出汁を飲みほした頃、彼も残った寿司をだいたい片付けて、やっぱり自分もうどんを食べるといって追加で注文した。</p> -]]></description> -</item> -<item> -<title>RP2040 SDKなし2 Clock、UART</title> -<link>https://www.mtkn.jp/computer/rp2040_2.html</link> -<guid>https://www.mtkn.jp/computer/rp2040_2.html</guid> -<pubDate>Tue, 27 Feb 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>RP2040 SDKなし2 Clock、UART</h1> -<time>2024-02-22</time><br /> -<time>2024-02-27</time>: リセット解除の間違いを修正。 <p> -今回はClockとUARTを設定してパソコンに繋ぎ、キーボードからの入力をオウム返しするプログラムを作成する。 +うどんの甘みをガリで消して店を出た。おごってくれた。</p> + <p> +弥一を後にし、和歌山城に案内した。和歌山城にはなぜか小さな動物園が付いている。時間がなかったので適当に見て天守閣に登った。地元の人間なのに天守閣はこれが初めてである。閉館ぎりぎりに行って30分しか見れなかった。天守閣は米軍に丸焼きにされたあと鉄筋コンクリート造りで再建された物である。外観はそれっぽいが内部は昭和の団地のような近代的なものだった。書画や武器等が展示されていてそれなりに見応えがあり、30分では足りなかった。彼は中国語が話せるので漢字も結構読める。僕が流し見した巻物を熱心に読んでいた。</p> + <p> -前回: <a href="rp2040_1.html">RP2040 SDKなしでLチカ</a><br> -ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex2 -</p> +和歌山城を出た後は寺か神社が見たいというので日前宮に行った。既に閉まっていて入れなかった。こんな時間に閉まっては会社帰りに立ち寄れないのではと言われた。ルーマニアではサラリーマンが帰りに教会に行くのが普通なのだろうか。</p> -<h2>動作環境</h2> -<ul> -<li>Void Linux - <ul> - <li>cross-arm-none-eabi-binutils-2.32_2</li> - <li>GNU Make 4.4.1</li> - <li>minicom version 2.7.1</li> - </ul> -</li> -<li><a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a> -</li> -</ul> - -<h2>Clock</h2> - -<h3>リング発振回路</h3> -<p>RP2040にはリング発振回路というのが内蔵されている。これは自分の出力を反転させようとするもので、不安定だが高速で消費電力の少ないクロックとして用いられる。RP2040は電源を入れると、このリング発振回路を動作用のクロックとして用いている。この発振回路の周波数は、チップの製造過程での誤差、動作時の電圧、動作温度によって変動するので、正確な周波数が必要な用途には向かない。</p> - -<h3>水晶発振子</h3> -<p>秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、より正確である。</p> +<p> +カフェにでも行って話しをしようということになった。いい喫茶店は知らないので和歌山市駅のスタバに行くことにした。駅ビルには確か日本酒のお店もあったのでそこで黒牛か超久でも買わせようと思った。ところが着いてみると平和酒造の店だった。この酒蔵はあまり好きではないので入らずに直接スタバに行った。スタバではどういう流れだったか、国境の話になった。昔は国という概念も国民という枠組みもなかったのに、いつしか作られて国境ができたという。ヨーロッパの世界はかつて神が治めていたが、あるときから貴族がその役割を担い、貴族が没落してからは民衆をまとめるために民族や国家というストーリーが必要になったという。そういえばゲルマン民族というのもナチスがドイツをまとめるために作った概念だとどこかで読んだ気がするが本当なのだろうか。日本は海に囲まれているのでヨーロッパと少し状況が違うんじゃないかという話を出してみた。言語も民族も一つで天然の国境があるのはヨーロッパ人から見るとちょっと変なのかな。そのくせ昔から文化的には国際色豊かだと言われた。</p> -<h3>PLL</h3> -<p>水晶振動子を入力として、周波数を数倍にしたものを出力するもの。電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」によると、入力周波数を<code>FREF</code>としたときの出力周波数は<code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。これらの変数はそれぞれ設定用のレジスタに値を保存することで変更できる。</p> +<p> +辺りがすっかり暗くなったころスタバを出て少しそのへんを散歩した。駅の周辺は全然土地勘がなかったが紀の川があるのは知っていたのでそっちに向かって適当に歩いた。日本語の練習をしたいので日本語で話して欲しいと言われた。僕の言葉は結構理解できていたようだし、向うも割と話せていた。多少ゆっくり話すようにはしたがそれでもたいしたものである。ところで外国人相手に母語を話すと普段どれだけ自分がいい加減に話しているかがよく分かる。発音や文法を間違えないように意識するととたんに言葉に詰まるのだ。</p> -<h2>UART</h2> <p> -Universal Asynchronous Receiver/Transmitterの略。2本の線だけで通信できる。プロトコルは詳しく知らないが、rp2040がよしなにやってくれる。rp2040では同時に二個まで利用できる。どのGPIOピンを使うかもある程度自由に選べる。どのピンが使えるかはデータシートの「2.19.2. Function Select」に書かれている。今回はGPIO0とGPIO1を使う。パソコンとの接続には、秋月電子通商で売っている<a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a>を使用した。UARTで接続するためのパソコン側のソフトウェアはminicomを使用した。僕の環境ではシリアル変換モジュールをパソコンにUSB接続すると、<code>/dev/ttyUSB0</code>として認識されるので、</p> -<pre><code>$ minicom -D /dev/ttyUSB0 -</code></pre> +人生で英語をしっかり話したのはこれが初めてである。彼と話していて思ったが、話したいことがあれば文章を組みたてる前に話しはじめたほうがいい。相手の話を理解したうえでなにか言いたげなことは伝わるし、そのあとは身振り手振りでも案外なんとかなる。外国人と話すのに外国語に関する知識はある程度必要だが、相手の話したいことに興味を持つのも重要である。</p> + <p> -とすると接続できる。 -</p> +河原では日本の山に危険な動物はいるのかという話になった。熊や毒蛇、スズメバチなんかが怖いと言った。このあたりで熊を見たことはないけど。それから彼がどうやって英語を流暢に話せるようになったのかも教えてくれた。英語でゲームをしていたら英語が第二の母語のようになったそうだ。ルーマニア語はマイナーな言語なのでゲームは翻訳されないらしい。僕も流暢ではないにしても何とか意思疎通できるようになったのは、英語でコンピューターの勉強をしたからだと思う。英語を勉強するより、英語を使ってなにかをすると、すぐ覚えるし忘れにくい。最後に二宮金次郎の像に集まった猫を眺めて駅に戻った。</p> -<h2>main.s</h2> -<h3>初期設定</h3> <p> -後で見るように、UARTの動作には多分水晶発振子とPLLが必要なので、まずはそれを設定する。起動後、メインのプログラムが読み込まれるまでの<code>boot2</code>は前回と同じものである。<code>main.s</code>ではまず前回と同様に初期スタックポインタとエントリーポイントを定義する: -</p> -<pre><code> .section .vectors -vectors: - .word 0x20040000 // initial SP - .word (reset+1) // entry point -</code></pre> +彼は明日の22時台の飛行機でイスタンブールを経由して国に帰る。イスタンブールまで12時間、乗り換えに3時間、そこからルーマニアまで1時間半、国に着いてからさらに12時間バスに乗る。なかなか遠いところである。国内線もあると言っていたが、日本でお金を使いすぎたから節約したいらしい。</p> + <p> -続いて利用するサブシステムのリセットを解除する。PLLとUARTが追加されている。今回使うUARTはUART0だけである。なお、UARTはclock_periが有効化されるまでリセット状態の解除が完了しないようなので、unreset_chkからは外してある:</p> -<pre><code> .section .text -reset: - // unreset gpio, pll_sys, uart0 - ldr r0, =(1 &lt;&lt; 22 | 1 &lt;&lt; 12 | 1 &lt;&lt; 5) // uart0 | pll_sys | io_bank0 - ldr r3, resets_base - ldr r1, atomic_clr - str r0, [r3, r1] // RESETS: RESET - mov r1, #1 - lsl r1, #22 - bic r0, r1 // uart stays in reset state until clock_peri is enabled -unreset_chk: - ldr r1, [r3, #0x8] // RESETS: RESET_DONE - bic r0, r1 - bne unreset_chk +彼は20:30発の特急サザンに乗って大阪のホテルに帰っていった。</p> +]]></description> +</item> +<item> +<title>使用しているハードウェア、ソフトウェア</title> +<link>https://www.mtkn.jp/computer/what-i-use.html</link> +<guid>https://www.mtkn.jp/computer/what-i-use.html</guid> +<pubDate>Mon, 28 Apr 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>使用しているハードウェア、ソフトウェア</h1> +<time>2025-04-28</time>更新<br> +<time>2021-12-13</time>作成 +<h2>ノートパソコン</h2> +<p>メインのパソコン。</p> +<dl> +<dt>ハードウェア: <a href="openbsd_on_u9310e.html">Fujitsu LIFEBOOK U9310/E</a></dt> +<dd>2013年製のMacBook Airを使っていたがバッテリーがへたってきた。ぱちもんのバッテリーは1年でだめになるのに7千円くらいするのでコスパが悪い。2025年4月に大阪日本橋で見付けたLIFEBOOKを買った。10世代のCore i5で27000円くらい。軽い。ディスプレイが綺麗、OpenBSDと相性がそこそこいい。</dd> +<dt>OS: OpenBSD</dt> +<dd> +使いやすい。 +</dd> -/* ... */ +<dt>Window Manager: <a href="https://git.mtkn.jp/dwm">dwm</a></dt> -atomic_clr: - .word 0x00003000 -resets_base: - .word 0x4000c000 -</code></pre> +<dt>Terminal: <a href="https://git.mtkn.jp/st">st</a></dt> -<h3>GPIOの設定</h3> -<p> -次にGPIOの役割を設定する。前回はLEDを点滅させるためにGPIO25をSIOに設定したが、今回はGPIO0とGPIO1をUART0にする: -</p> -<pre><code> // set gpio functions - ldr r3, io_bank0_base - mov r0, #2 // uart0 - mov r1, #0x4 - str r0, [r3, r1] // IO_BANK0: GPIO0_CTRL - mov r1, #0xc - str r0, [r3, r1] // IO_BANK0: GPIO1_CTRL +<dt>Text Editor: acme, nvi</dt> +<dd>vimは辞めた。OpenBSDにあったnviが丁度いい。日本語を使うので日本語入力が必要ない場面ではacmeを使っている。</dd> -/* ... */ +<dt>Browser: firefox</dt> +<dd>もうちょっとましなのないんかな。UIころころ変わるし、重いし、嫌い。JavaScriptがないと困るので仕方なく使っているが...特に最近画面上部のUIが大きすぎて邪魔である。</dd> -io_bank0_base: - .word 0x40014000 -</code></pre> +<dt>Mail Client: neomutt</dt> +<dd>あんまり好きじゃない</dd> +</dl> -<h3>Clockの設定</h3> -<p> -Clockの設定をする。まずは水晶発振子を起動する。水晶発振子は起動してから周波数が安定するまで少し時間がかかるようで、その間待たないといけない。この時間は1msあれば十分だとデータシートに書いている。この待ち時間はXOSC: STARTUPレジスタに、256サイクル単位で記述する。データシートによると初期のリング発振子は最大で12MHzなので、<code>(12 * 10^6 * 1 * 10^-3) / 256 = 47</code>をこのレジスタにセットする。ところでデータシートではこの計算はリング発振子ではなく水晶発振子の周波数で書かれている。起動直後でまだ使えない水晶発振子の周波数を使うのはなんでやろ。SDKでも<code>pico-sdk/src/rp2_common/hardware_xosc/xosc.c</code>で、 -</p> -<pre><code>#define STARTUP_DELAY (((((XOSC_MHZ * MHZ) / 1000) + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER) -</code></pre> -<p> -と定義されている(PICO_XOSC_STARTUP_DELAY_MULTIPLIERは1)。とりあえず47に設定しているが、試しに0や1にしても動いた。よくわからん。</p> -<p> -待ち時間を設定したら発振子を起動する。XOSC: CTRLに起動用のコマンド的なものを入力し、周波数が安定するのを待つ。</p> +<h2>デスクトップ</h2> +<p>学生のときに組んだ。無駄にハイスペック。この上RTX2060も積んでたが、OpenBSDで使えないので解雇した。最近は絵を描くのに使っている。</p> +<dl> +<dt>マザーボード: ASRock Z390 Pro4</dt> +<dt>CPU: Intel Core i7-9700K</dt> +<dt>メモリ: センチュリーマイクロ 8GB * 2</dt> +<dd>国産</dd> +<dt>ストレージ: KimMiDi SSD 256GB</dt> +<dd>ThinkPad買ったときについてきたやつ。</dd> +<dt>モニタ: Eizo FlexScan EV2451</dt> +<dd>綺麗やけど質量が大きい。</dd> +<dt>OS: OpenBSD</dt> +<dd></dd> +</dl> + +<h2>家のサーバー</h2> <p> -以上を実装したのが以下のコード: +主にDNSとして使っている。 </p> -<pre><code> // setup xosc - ldr r3, xosc_base - mov r0, #47 // start up delay for 12MHz rosc (xosc?) - str r0, [r3, #0xc] // XOSC: STARTUP - ldr r0, =(0xfab &lt;&lt; 12 | 0xaa0) - str r0, [r3, #0] // XOSC: CTRL -wait_xosc: - ldr r0, [r3, #0x4] // XOSC: STATUS - lsr r0, r0, #31 // STABLE bit - beq wait_xosc - -/* ... */ +<dl> +<dt>ハードウェア: Dell D520</dt> +<dd>おじいちゃんの家にころがってたのをもらってきた。</dd> +<dt>OS: OpenBSD</dt> +<dd></dd> +</dl> -xosc_base: - .word 0x40024000 -</code></pre> +<h2>ウェブサーバー</h2> +<p>このウェブページ用</p> +<dl> +<dt>VPS: さくらのVPS</dt> +<dd>ちょっと高い(7078円/12ヶ月)。OpenBSDを入れられる。</dd> +<dt>OS: OpenBSD</dt> +<dt>httpサーバー: Openbsd httpd</dt> +<dd>manが分かりやすい。</dd> +<dt>smtpサーバー: OpenSMTPD</dt> +<dd>manが分かりやすい。</dd> +<dt>imapサーバー: dovecot</dt> +<dd>manはあんまり分かり易くない。<dd> +</dl> +]]></description> +</item> +<item> +<title>OpenBSD on Fujitsu LIFEBOOK U9310/E</title> +<link>https://www.mtkn.jp/computer/openbsd_on_u9310e.html</link> +<guid>https://www.mtkn.jp/computer/openbsd_on_u9310e.html</guid> +<pubDate>Mon, 28 Apr 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>OpenBSD on Fujitsu LIFEBOOK U9310/E</h1> +<time>2025-04-28</time> -<h3>PLLの設定</h3> +<h2>はじめに</h2> <p> -水晶発振子が起動できたので、次にPLLを設定する。CPUが133MHzまで対応しているので133MHzになるようにした。</p> +大阪の日本橋で状態のいいLIFEBOOKを見つけたので購入した。OpenBSDをインストールしたらおおむね問題なく動いた。</p> + +<h2>ハードウェア</h2> +<img src="images/openbsd_on_u9310e_1.jpg" alt="U9310/E"> <p> -PLLは入力となる振動(ここでは水晶発振子の振動)を加工して周波数を上げたり下げたりする。出力の周波数は以下の式で決まる: -</p> -<pre>(FREF / REFDIV) * FBDIV / (POSTDIV1 * POSTDIV2)</pre> +FujitsuのLIFEBOOK U9310/Eというパソコンである。2020年にビジネス向けに発売されていたもので、リース会社から放出されたものだと思う。CPUはIntelの第10世代のCore i5 10310U、メモリは8GB、ストレージはKIOXIA製NVMeの256GB。メモリは増設できないがWindowsなんてものは使わないので十分である。ディスプレイは多分IGZOのFull HDでめちゃ綺麗。キーボードは必要十分かなあ。ただし特定の組み合わせのキーを同時に入力することができない。A、W、Dが同時に入力できないのでゲームするには少し困りそう。またCapsLock、Shift、PgUpの組み合せもだめだった。CapsLockにはCtrlを割り当てており、この組み合わせでstの文字を拡大するようにしていたのでこれが使えないのは不便である。stの設定をいじって回避した。タッチパッドもまあまあ。左右クリック用に物理ボタンがあるのは凄くいい。でもThinkpadみたいにタッチパッドの上に付いてた方がホームポジションから動かなくていいので好き。ファンは結構静かだと思う。YouTubeを開くと少し回るがすぐに静かに戻る。今回のものはキーボードにバックライトがないモデルだった。少し不便だがすぐ慣れると思う。</p> + +<h2>内部</h2> <p> -FREFは入力の周波数(ここでは12MHz)で、その他の変数はプログラマが設定できる。ただしデータシートによると(FREF / REFDIV)は5MHz以上でないといけないので、REFDIVは1である。また、FBDIVは16〜320、POSTDIV1とPOSTDIV2は1〜7で、POSTDIV1とPOSTDIV2に違う値を代入する場合、POSTDIV1に大きい方を入れたほうが消費電力が少なくなるとのことなので、133MHzにするには、FBDIV=133、POSTDIV1=6、POSTDIV=2とすればいい(POSTDIV1=4、POSTDIV2=3も可能だが、pico-sdkに付属するvcocalc.pyというスクリプトのコメントには、この2つの値の差が大きい方がいいと書いている)。 -</p> +分解は簡単。電源を落して、裏面にあるネジを全部外せば裏蓋が開く。ツメで引っ掛けていたりしないので外しやすい。裏蓋にはバッテリーの絵が描かれたボタンがあって、いかにも押してほしそうな顔をしていたので一応押してから分解した。組み立てた後は電源ケーブルを繋がないと電源が入らなかった。おそらくバッテリーとの接続を切断するためのボタンなのだと思う。</p> +<img src="images/openbsd_on_u9310e_2.jpg" alt="U9310/E の内部"> <p> -PLL設定の手順は、FBDIVの設定、PLLとVCOの起動、VOCが安定するまで待機、POSTDIV1とPOSTDIV2の設定、Post Dividerの起動、そして最後にシステムとUARTのクロックを今設定したPLLに変更、である。以上を実装したのが以下のコード: -</p> -<pre><code> // setup pll_sys 133MHz - ldr r3, pll_sys_base - // set feedback divider - mov r0, #133 - str r0, [r3, #0x8] // PLL: FBDIV_INT - // power on pll and vco - ldr r0, =(1 &lt;&lt; 5 | 1) // VCOPD | PD - ldr r1, atomic_clr - add r1, r1, #0x4 - str r0, [r3, r1] // PLL: PWR - // wait vco to lock -wait_vco: - ldr r0, [r3, #0] // PLL: CS - lsl r0, r0, #31 - beq wait_vco - // setup post dividers - ldr r0, =(4 &lt;&lt; 16 | 3 &lt;&lt; 12) - str r0, [r3, #0xc] // PLL: PRIM - // power on post divider - mov r0, #8 // POSTDIVPD - str r0, [r3, r1] // PLL: PWR - - // set system clock clksrc_pll_sys - ldr r3, clocks_base - ldr r0, =(0x0 &lt;&lt; 5 | 0x1) - str r0, [r3, #0x3c] // CLOCKS: CLK_SYS_CTRL - // enable clk_peri - mov r0, #1 - lsl r0, r0, #11 - str r0, [r3, #0x48] // CLOCKS: CLK_PERI_CTRL +CPUクーラー、ヒートシンク、Wi-Fiカード、WWANカード、SSDは簡単に外せた。他のものもだいたい分解しやすそうである。電源プラグが交換できるのはポイントが高い。上でも書いたがメモリははんだ付けされていて交換できない。キーボードも外せそうではあるがマザーボードを取ったうえネジが沢山あったのでやめにした。</p> +<h2>OpenBSDのサポート</h2> +<table> +<tr> +<th>機能</th> +<th>サポート</th> +<th>備考</th> +</tr> -/* ... */ +<tr> +<td>音</td> +<td>○</td> +<td> +Intel 400 Series HD Audio、コーデック: Realtek ALC255。<br>azaliaによりサポート。メディアキーによる音量調整が可能。</td> +</tr> -atomic_clr: - .word 0x00003000 -clocks_base: - .word 0x40008000 -pll_sys_base: - .word 0x40028000 -</code></pre> +<tr> +<td>バッテリーの状態</td> +<td>○</td> +<td> +acpibatによりサポート。 +</td> +</tr> -<h3>UARTの設定</h3> -<p> -データシートによるとUART設定の手順は以下の通り: -</p> -<ul> -<li>リセットの解除</li> -<li>clock_periの設定</li> -<li>UARTの有効化</li> -<li>FIFOの有効化</li> -<li>転送速度の設定</li> -<li>フォーマットの設定</li> -</ul> -<p> -上の2つは既に終えている。残りの部分はこの順番どおりに設定しても動かなかった。C言語で書かれたサンプルを見ると、クロックを設定した後、転送速度の設定、UARTの有効化、FIFOの有効化の順になっている。そのとおりにすると動いた。理由はよく理解していないが、変数を設定してから起動するほうが素直ではある。</p> -<p> -転送速度はminicomのデフォルトである115200 baudに設定する。データシート「4.2.7.1. Baud Rate Calculation」の計算式において、クロック周波数を125MHzから133MHzに変えて計算して、BRDI=72、BDRF=0.157(=10/64)となる。この数値をUART: UARTIBRD、UART: UARTFBRDレジスタにそれぞれ代入する。 -</p> -<p> -UARTの有効化はUART: UARTCRレジスタのUARTENビットをセットすることで行う。C言語のサンプルでは同じレジスタのRXE、TXEビットもセットしているが、この2つはもともと1になっているのでほっといてよさそう。</p> -<p> -FIFOの有効化はUART: UARTLCR_HレジスタのFENビットをセットすることで行う。また、同じレジスタの他のビットで、データーのフォーマットを設定できる。ここではminicomのデフォルトに合わせてWLENを8bitにする。</p> -<p> -以上をまとめると以下のようになる: -</p> -<pre><code> // setup uart0 - ldr r3, uart0_base - // set baudrate 115200 - // BDRI = 72, BDRF = 0.157 (10 / 64) - mov r0, #72 - str r0, [r3, #0x24] // UART: UARTIBRD - mov r0, #10 - str r0, [r3, #0x28] // UART: UARTFBRD - // enable uart0 - mov r0, #1 // UARTEN - ldr r1, atomic_set - add r1, r1, #0x30 - str r0, [r3, r1] // UART: UARTCR - // enable FIFO and set format - ldr r0, =(3 &lt;&lt; 5 | 1 &lt;&lt; 4) // WLEN = 8, FEN = 1 - str r0, [r3, #0x2c] // UART: UARTLCR_H +<tr> +<td>キーボードバックライト</td> +<td>?</td> +<td> +今回購入したものには搭載されていなかった。 +</td> +</tr> -/* ... */ +<tr> +<td>ハイバーネーション</td> +<td>○</td> +<td> +ZZZが使える。 +</td> +</tr> -atomic_set: - .word 0x00002000 -uart0_base: - .word 0x40034000 -</code></pre> +<tr> +<td>スリープ</td> +<td>○</td> +<td> +zzzが使える。液晶を閉じると自動でスリープし、開くと問題なく復帰できる。電源ランプが点滅するのが少しじゃま。 +</td> +</tr> -<h3>UARTの入出力</h3> -<p> -設定が終わったので実際にUARTの入出力を処理するコードを書く。まずUARTからの出力は、出力したいバイトをUART: UARTDRに書き込むことで行う。その際、書き込まれたデータは一時的に出力用FIFOに保持されるので、このFIFOが満杯でないことを確認する必要がある。FIFOの状態はUART: UARTFRレジスタで確認できる。このレジスタのTXFFの値が1であればデータを書き込めないので、0になるまで待機する。関数名は<code>putbyte</code>にした。また出力したいデータは<code>r0</code>レジスタにの下位8ビットに入れられているものとした。書き込めるデーターは8ビットだけなので、<code>0xff</code>と論理積をとってから書き込んでいる: -</p> -<pre><code>putbyte: - ldr r3, uart0_base - mov r1, #1 - lsl r1, r1, #5 // TXFF -txff: - ldr r2, [r3, #0x18] // UART: UARTFR - tst r1, r2 - bne txff - mov r1, #0xff - and r0, r0, r1 - str r0, [r3, #0] // UART: UARTDR - bx lr +<tr> +<td>タッチパッド</td> +<td>○</td> +<td> +imtでサポート。 +</td> +</tr> -/* ... */ +<tr> +<td>USB type-A</td> +<td>○</td> +<td> +使えた。 +</td> +</tr> -uart0_base: - .word 0x40034000 -</code></pre> +<tr> +<td>USB type-C、Thunderbolt</td> +<td>?</td> +<td> +充電はできた。Thunderboltはケーブルがないのでテストできない。</td> +</tr> -<p> -入力はUART: UARTDRの下位8ビットを読むことで得られる。UARTからの入力は、一時的に入力用FIFOに保存される。このFIFOが空の状態でデータを読んでも意味がないので、FIFOが空でないことを確認する必要がある。これはUART: UARTFRレジスタのRXFEを読むことで確認できる。本来は入力があったときに割り込みを発生させて、それまではCPUを休ませるか別の処理をさせておくべきだが、とりあえずここではループでFIFOの状態を確認し続けている。関数名は<code>getbyte</code>にした。 -読み込んだデータは<code>r0</code>レジスタに保存している:</p> -<pre><code>getbyte: - ldr r3, uart0_base - mov r1, #1 - lsl r1, r1, #4 // RXFE -rxfe: - ldr r2, [r3, #0x18] // UART: UARTFR - tst r1, r2 - bne rxfe - ldr r0, [r3, #0] // UART: UARTDR - mov r1, #0xff - and r0, r0, r1 - bx lr +<tr> +<td>SDカードリーダー</td> +<td>○</td> +<td> +使えた。 +</td> +</tr> -/* ... */ +<tr> +<td>映像出力</td> +<td>○</td> +<td> +inteldrmでサポート。輝度の調整はxbacklightで出来るが、メディアキーは使えなかった。HDMIの出力も問題ない。 +</td> +</tr> -uart0_base: - .word 0x40034000 -</code></pre> -<p> -あとはこの2つの関数をループの中で交互に呼び出せば、オウム返しするだけのプログラムが完成する: -</p> -<pre><code>loop: - bl getbyte - bl putbyte - b loop -</code></pre> +<tr> +<td>ウェブカメラ</td> +<td>○</td> +<td> +SunplusIT Inc FJ Camera。uvideoでサポート。 +</td> +</tr> -<h2>リング発振回路でUARTは動くんかな?</h2> -<p>UARTの通信には正確なクロックが必要である。その為上では<code>clk_peri</code>として水晶発振子とPLLを用いた。ところがpico-examplesのhello_uartでは<code>main()</code>関数で水晶発振子を設定していない。そこでリング発振回路を用いてみたのだが、どうもうまく通信できない。出力されている正確な周波数も分からないのであきらめることにした。オシロスコープなんていうものは持っていない。</p> +<tr> +<td>Ethernet</td> +<td>○</td> +<td> +emでサポート。 +</td> +</tr> -<h3>pico-sdk</h3> -<p> -ところがどうも調べているとSDKを使った場合、デフォルトではクロック周波数は125MHzになっているらしい。どうやら水晶発振子もPLLも<code>main()</code>が呼ばれる前に設定されているようである。</p> -<p> -pico-examplesのサンプルプログラムはビルドすると自動で逆アセンブリしたファイルを出力してくれる。これを見ると、最初の256バイトは前回説明したboot2のコードで、その後ろにベクターテーブルが続く。ベクターテーブルの最初は初期スタックポインタで、<code>0x20042000</code>になっている。次はエントリーポイントで、<code>0x100001f7</code>である:</p> -<pre><code>10000100 &lt;__VECTOR_TABLE&gt;: -10000100: 20042000 .word 0x20042000 -10000104: 100001f7 .word 0x100001f7 -</code></pre> -<p> -Thumbモードなので実際のエントリーポイントは<code>1</code>引いた、<code>0x100001f6</code>である。この場所ではまず自分のCPUIDを調べて、<code>1</code>であれば待機状態に移行する。RP2040はデュアルコアである。起動直後はCPUIDが<code>0</code>のコアだけで処理をして、CPUIDが<code>1</code>のコアはプログラマが必要に応じて起動することになっている。このためCPUIDが<code>1</code>のコアは起動してすぐに待機状態に入ることがデータシートに書かれている。しかしこの処理はユーザーの書いたプログラムじゃなくて内蔵ROMにある起動用プログラムが担当するみたいに書かれてるんやけど、なんでSDKではユーザープログラムの一部として組み込んでるんかな? -</p> -<pre><code>100001f6 &lt;_reset_handler&gt;: -100001f6: 481d ldr r0, [pc, #116] ; (1000026c &lt;hold_non_core0_in_bootrom+0xe&gt;) -100001f8: 6800 ldr r0, [r0, #0] -100001fa: 2800 cmp r0, #0 -100001fc: d12f bne.n 1000025e &lt;hold_non_core0_in_bootrom&gt; -</code></pre> -<p>上のコードの最初の<code>ldr</code>は、<code>0xd0000000</code>(M0PLUS: CPUIDレジスタ)をロードしている。最後の飛び先<code>0x1000025e</code>はCPUIDが<code>1</code>のCPUを待機させる処理である:</p> -<pre><code>1000025e &lt;hold_non_core0_in_bootrom&gt;: -1000025e: 4809 ldr r0, [pc, #36] ; (10000284 &lt;hold_non_core0_in_bootrom+0x26&gt;) -10000260: f001 fb9c bl 1000199c &lt;rom_func_lookup&gt; -10000264: 4700 bx r0 -10000266: 0000 .short 0x0000 -/* ... */ -10000284: 00005657 .word 0x00005657 -</code></pre> -<p>内蔵フラッシュに書きこまれた関数を呼びだしている。呼びだしに使うコードは<code>0x00005657</code>(<code>'W' | 'V' &lt;&lt; 8</code>)である。データシートを見ると、この関数は<code>_wait_for_vector()</code>という名前で、CPUIDが1のCPUを寝かしつけるのに使われると書いている。この部分のソースコードをpico-sdkで探すと<code>pico-sdk/src/rp2_common/pico_standard_link/crt0.S</code>というのが見付かった:</p> -<pre><code>$ find pico-sdk/src -type f | xargs grep -l _reset_handler -pico-sdk/src/rp2_common/pico_standard_link/crt0.S -</code></pre> -<p>このファイルによると: -</p> -<pre><code> // Only core 0 should run the C runtime startup code; core 1 is normally - // sleeping in the bootrom at this point but check to be sure -</code></pre> -<p>だそうである。やっぱり無駄やん。内蔵フラッシュのプログラムにバグがあってもこのコードのせいで見付かりにくくなってない?知らんけど。</p> +<tr> +<td>Wi-Fi</td> +<td>○</td> +<td> +Intel Wi-Fi 6 AX201。iwxでサポート。 +</td> +</tr> -<p>続いて<code>.data</code>領域と<code>.bss</code>領域のコピー、初期化のようである。多分OSの本かなんかで習ったメモリマップの話:</p> -<pre><code>100001fe: a40d add r4, pc, #52 ; (adr r4, 10000234 &lt;data_cpy_table&gt;) -10000200: cc0e ldmia r4!, {r1, r2, r3} -10000202: 2900 cmp r1, #0 -10000204: d002 beq.n 1000020c &lt;_reset_handler+0x16&gt; -10000206: f000 f812 bl 1000022e &lt;data_cpy&gt; -1000020a: e7f9 b.n 10000200 &lt;_reset_handler+0xa&gt; -1000020c: 4918 ldr r1, [pc, #96] ; (10000270 &lt;hold_non_core0_in_bootrom+0x12&gt;) -1000020e: 4a19 ldr r2, [pc, #100] ; (10000274 &lt;hold_non_core0_in_bootrom+0x16&gt;) -10000210: 2000 movs r0, #0 -10000212: e000 b.n 10000216 &lt;bss_fill_test&gt; +<tr> +<td>WWAN</td> +<td>?</td> +<td> +Sierra Wireless, Incorporated EM7430。umbにより認識はされてる。ifconfigでもネットワークインターフェースとして表示されてる。simカード持ってないから試せない。 +</td> +</tr> -10000214 &lt;bss_fill_loop&gt;: -10000214: c101 stmia r1!, {r0} +<tr> +<td>Bluetooth</td> +<td>×</td> +<td> +OpenBSDはBluetoothが嫌い。ugenにより認識はされてる。 +</td> +</tr> -10000216 &lt;bss_fill_test&gt;: -10000216: 4291 cmp r1, r2 -10000218: d1fc bne.n 10000214 &lt;bss_fill_loop&gt; -</code></pre> +</table> +]]></description> +</item> +<item> +<title>大阪万博行った</title> +<link>https://www.mtkn.jp/journal/posts/20250420.html</link> +<guid>https://www.mtkn.jp/journal/posts/20250420.html</guid> +<pubDate>Wed, 23 Apr 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>大阪万博行った</h1> +<time>2025-04-20</time> -<p>最後にいろいろ呼びだす:</p> -<pre><code>1000021a &lt;platform_entry&gt;: -1000021a: 4917 ldr r1, [pc, #92] ; (10000278 &lt;hold_non_core0_in_bootrom+0x1a&gt;) -1000021c: 4788 blx r1 -1000021e: 4917 ldr r1, [pc, #92] ; (1000027c &lt;hold_non_core0_in_bootrom+0x1e&gt;) -10000220: 4788 blx r1 -10000222: 4917 ldr r1, [pc, #92] ; (10000280 &lt;hold_non_core0_in_bootrom+0x22&gt;) -10000224: 4788 blx r1 -10000226: be00 bkpt 0x0000 -10000228: e7fd b.n 10000226 &lt;platform_entry+0xc&gt; -/* ... */ -10000278: 10001819 .word 0x10001819 -1000027c: 100002dd .word 0x100002dd -10000280: 10001909 .word 0x10001909 -</code></pre> -<p>一つめの<code>blx</code>は<code>0x10001818</code>(<code>runtime_init</code>)を、二つめは<code>0x100002dc</code>(<code>main</code>)を、最後のは<code>0x10001908</code>(<code>exit</code>)を、それぞれ呼んでいる。この<code>runtime_init</code>はアセンブリでは分かりにくいのでソースコードを探してみると、以下のものが見付かった:</p> -<pre><code>$ find pico-sdk/src -type f | xargs grep -l runtime_init -pico-sdk/src/rp2_common/pico_runtime/runtime.c -pico-sdk/src/rp2_common/pico_standard_link/crt0.S -pico-sdk/src/common/pico_sync/include/pico/mutex.h -</code></pre> -<p>最後の<code>mutex.h</code>は関係なさそう。二つめの<code>crt0.S</code>は呼びだしてるだけ。一つめの<code>runtime.c</code>が多分探しているものである。これを見るとまず各種周辺機器を一度リセットし、リセット状態を解除している。使わんやつも初期化してない?その後<code>clocks_init()</code>を呼んでいる。この関数は<code>pico-sdk/src/rp2_common/hardware_clocks/clocks.c</code>で定義されている。これを見ると、<code>xosc_init()</code>を呼んで水晶発振子を初期化した後、<code>clk_peri</code>を125MHzに設定している:</p> -<pre><code> clock_configure(clk_peri, - 0, - CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, - 125 * MHZ, - 125 * MHZ); -</code></pre> -<p>やっぱり水晶発振子じゃないとあかんのかな。</p> +<p>4月15日火曜日に大阪万博に行ってきた。 +</p> -<h2>CMake</h2> -<p>上ではビルドしたバイナリを逆アッセンブルして読んだ。わざわざこんなことをしなくてもMakefile読めばなにがどうなって最終生成物に辿りつくのか分かればいいのだが、そうもいかない。このSDKとpico-examplesにはビルドシステムとしてCMakeなるものが使われている。これがどうも複雑でよく分からない。勉強する気にもならん。上で見た<code>crt0.S</code>や<code>runtime.c</code>といったファイルも<code>hello_uart</code>で本当に使われているものなのかもよく分からない。こんな煩雑なものは本当に必要なのかな。無駄に複雑にしてるだけとちゃうんかな。特に僕は勉強用に使ってるので、ソースコードの依存関係をもっと分かりやすくしてくれないと、内部でなにがどうなってるのか理解しにくい。何度か頑張って読もうとしたが、面白くないのでやめた。数百行のファイルをあっちからこっちから<code>include</code>してるし、大文字ばかりの変数だらけで目が痛い。こんなものを扱えるというのはえらいええ頭してはるんやね。</p> +<p>朝の紀州路快速は高校生ばかりだった。大阪に近付くにつれてビジネスマンが多くなっていった。西九条から地下鉄に乗りかえるとEXPO staffの札を首から下げた外国人がたくさんいた。外国人ばかりなのになぜかみんなスマホを見たり、目が合ってもなんとなく外を向いたりと、雰囲気だけが日本なのが面白い。</p> +<p> +会場には予約より少し早く着いた。荷物検査があるので結構時間がかかり、入れたのは予約より30分ほど過ぎたころだった。</p> +<figure> +<img src="20250420-1.jpg" alt="東ゲート"> +<figcaption>東ゲート</figcaption> +</figure> +<figure> +<img src="20250420-2.jpg" alt="リング"> +<figcaption>リング</figcaption> +</figure> -<h2>参考</h2> -<ul> -<li> -<a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> -</li> -<li> -<a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> -</li> -<li> -<a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> -</li> -<li> -<a href="https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%82%B7%E3%83%AC%E3%83%BC%E3%82%BF">リング・オシレータ.Wikipedia</a> -</li> -<li> -<a href="https://www5.epsondevice.com/ja/information/technical_info/osc.html">水晶発振器とは? 原理と仕組み、水晶振動子との違い、選び方のポイントを解説.エプソン水晶デバイス</a> -</li> -</ul> +<p>会場内はところどころ植え込みがあって雑草も生えてきていた。タンポポは全部セイヨウだった。中央付近に静けさの森という場所があるが、そんなに森にはなっていない。どうせなら明治神宮みたいなのを作ればよかったかと思う。IR会場になった後も公園にできるし。 +</p> +<figure> +<img src="20250420-3.jpg" alt="セイヨウタンポポ"> +<figcaption>セイヨウタンポポ</figcaption> +</figure> +<figure> +<img src="20250420-4.jpg" alt="ノゲシ"> +<figcaption>ノゲシ</figcaption> +</figure> +<figure> +<img src="20250420-5.jpg" alt="バラ科のなにか"> +<figcaption>バラ科のなにか</figcaption> +</figure> +<figure> +<img src="20250420-6.jpg" alt="ノボロギク"> +<figcaption>ノボロギク</figcaption> +</figure> +<figure> +<img src="20250420-7.jpg" alt="ノアザミ"> +<figcaption>ノアザミ</figcaption> +</figure> +<figure> +<img src="20250420-8.jpg" alt="タネツケバナ"> +<figcaption>タネツケバナ</figcaption> +</figure> +<figure> +<img src="20250420-9.jpg" alt="ヨモギ"> +<figcaption>ヨモギ</figcaption> +</figure> + +<p>11時に英国パビリオンを予約していたのだが着いてみるとシステムエラーで閉鎖されていた。レストランは開いていたので混む前になにか食べることにした。英国人と会話できると思っていたが案内してくれたのは片言の英語を喋るアジア人のおばちゃんだった。2個で9ポンドのアイスクリームは売ってなかった。フィッシュアンドチップスと紅茶を頼んだ。出された紅茶にはミルクが付いていなかった?!フィッシュアンドチップスの方は思っていたよりは美味しかった。味のしない鱈だったが鮮度は良く、衣もさくさくだった。油もそこまで古くなっていない。チップスの方はモスバーガーよりイモの味が薄かった。タルタルソースと緑のペーストが付いていた。緑のものは多分エンドウ豆を潰したものにミントで香りを付けていた。悪くはなかったが組合せが初めてだったせいか、ミントが目立ちすぎていたように思う。</p> +<figure> +<img src="20250420-10.jpg" alt="フィッシュアンドチップス"> +<figcaption>フィッシュアンドチップス</figcaption> +</figure> + +<p>英国パビリオンが復旧するまで他を周ることにした。オーストリアに行ってみたが列が一杯だから時間をおいて来るように言われた。</p> + +<p>大根が咲いていた。 +</p> +<figure> +<img src="20250420-11.jpg" alt="ハマダイコン"> +<figcaption>ハマダイコン</figcaption> +</figure> + +<p>二つほど空いてる所を見たがあんまり面白くなかった。</p> + +<p>英国に戻ってみると復旧していたので入ることにした。前に英国人の親子が並んでいた。母親が高校生くらいの息子にハグして日本語で「トテモカワイイデス」と言っていた。突然日本語が聞こえてくるとどうしても見てしまう。目が合った。その後もなぜかちらほら日本語が聞こえてきては目が合った。しばらくして父親の方が息子の鞄からチョコ棒を出して母親と息子に配った。自分の分を取ったあとなぜかさらにもう一本取り出してこっちに差し出してきた。話し掛けたいなあと思っていた所だったので受け取ってお礼を言って何を話そうかと考えていたのだが、パビリオンの扉が開いて3人は吸い込まれていった。パビリオンは20人程度のグループで周るようになっていたのだが、丁度僕の前で区切られてしまったのだ。</p> +<figure> +<img src="20250420-12.jpg" alt="英国人がくれたチョコ棒"> +<figcaption>英国人がくれたチョコ棒</figcaption> +</figure> + +<p>展示は特に面白くなかった。特定の医薬品会社がお金を出していそうなことは分った。 +</p> + +<p>パビリオンの中にバーがあった。ビールでも飲みたかったが外が寒すぎて冷えていたのであきらめた。 +</p> + +<p>その後は特に予定もなかったので散歩していたらルーマニアパビリオンを見つけた。チェロの先生がルーマニアで修行してきた人なので行ってみたかったのだが、予約のフォームで見つからなかったので来ていないものだと思っていた。予約できないだけみたいだ。</p> + +<p>30分ほど並んだ。入口に居たお姉さんがめっちゃ綺麗だった。展示は生演奏と伝統工芸の展示、実演だった。演奏は僕が行ったときはバイオリンだけだったが、椅子と譜面台が4つとピアノもあったので時間によって編成が変わるようだった。伝統工芸の展示のひとつに、ガラスに描かれた宗教画があった。ビザンツからの伝統だと言っていたと思う。絵の具が置いてあったので描かせてもらった。マリアがキリストを抱いている画を模写した。描きながらルーマニアのことを色々教えてもらえたのがとても良かった。女の人が全員綺麗だった。さすが東欧である。レストランもあったが残念ながら閉まっていて食べられなかった。</p> + +<p>最後に知りあったルーマニア人とその辺をぶらぶらして帰ってきた。</p> +<figure> +<img src="20250420-13.jpg" alt="知り合ったルーマニア人"> +<figcaption>知り合ったルーマニア人</figcaption> +</figure> + +<p>パビリオンの展示は総じていまいちである。今回行った海外のパビリオンには特に未来っぽいものはなにもなかった。これはある意味期待通りだった。一方でルーマニアでは現地の文化も見れたし現地人との交流もできたのでよかった。他のパビリオンでもこういう交流があればいいのにと思う。国内企業のパビリオンに行けていないので次回はその辺を見たい。</p> ]]></description> </item> <item> -<title>RP2040 SDKなしでLチカ</title> -<link>https://www.mtkn.jp/computer/rp2040_1.html</link> -<guid>https://www.mtkn.jp/computer/rp2040_1.html</guid> -<pubDate>Sun, 25 Feb 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>RP2040 SDKなしでLチカ</h1> -<time>2023-04-25</time>: 作成<br /> -<time>2024-02-25</time>: ベクターテーブルの修正 - -<h2>はじめに</h2> +<title>着物作った</title> +<link>https://www.mtkn.jp/journal/posts/20250221.html</link> +<guid>https://www.mtkn.jp/journal/posts/20250221.html</guid> +<pubDate>Fri, 21 Feb 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>着物作った</h1> +<time>2025-02-21</time> <p> -パタヘネのRISC-V<sup>[1]</sup>版を買って一通り読んだらアセンブリ言語で組込のプログラミングがしたくなった。RISC-Vのマイコンボードが欲しかったのだが、安くていい感じのものが見付からなかった。代わりに秋月電子通商でArmのものがあった。RP2040マイコンボードキット<sup>[2]</sup>というものである。ウェブ上の情報も多く、データシート<sup>[3]</sup>もしっかりしていそうなので、とりあえずこれを買ってみた。</p> +普段着回している着物がへたってきたので新しいのを仕立てた。楽天で見付けた真綿紬の草木染めである。中古で2640円だった。新品だとおそらく何十万のしろものである。女物だったので解いて洗濯し、男物に仕立て直した。防虫剤の臭いがひどかったが、洗濯すればおおむね取れた。</p> + <p> -一般的にはSDK<sup>[4]</sup>をダウンロードしてあらかじめ用意されたライブラリを使って開発するようだが、これはビルドシステムとしてcmakeというのを使っている。これがOpenBSDでは何かエラーがでて動かなかった。僕はこういう便利ツールが嫌いだ。どうせ使わんからいいんやけど。関係ないけど途中から開発環境がLinuxに替わった。SDKには便利な関数がたくさん用意されているので楽である。ハードウェアの面倒な部分がプログラマから見えないようにしているからである。しかし今回はその面倒な部分に触れてみたくて買ったので、SDKを使うと意味がない。</p> +解いた布を重ねてネットに入れ、普通に洗濯する。脱水が済んだら濡れた状態でアイロンをかけてしわをとり陰干しする。絹ものだがこれで特に問題なく洗濯できた。</p> + <p> -ということでSDKなしで開発してみる。とりあえず定番のLチカをば。</p> +裏地も一緒に洗濯した。これも多分羽二重の高価なものである。ただ袷にするのは面倒なので一重にした。袖付けがなかなか上手くいかずに人形のところが皺になる。居敷当ては箪笥に眠っていた唐草模様の風呂敷を使った。</p> + +<figure> +<img src="20250221.jpg" alt="作った着物"> +</figure> + <p> -ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a> -</p> +真綿紬は初めてだが、軽くて暖かい。ほどかずに洗濯してよれよれにならなければいいのだが...</p> +]]></description> +</item> +<item> +<title>きのかわ弦楽合奏団 さくらコンサート</title> +<link>https://www.mtkn.jp/journal/posts/20250216.html</link> +<guid>https://www.mtkn.jp/journal/posts/20250216.html</guid> +<pubDate>Sun, 16 Feb 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>きのかわ弦楽合奏団 さくらコンサート</h1> +<time>2025-02-16</time> +<p>三月にコンサートあります。</p> +<figure> +<img src="20250216-1.jpg" alt="きのかわ弦楽合奏団のさくらコンサートのちらしの表面。令和7年3月23日14:00から、旧和歌山県議会議事堂にて"> +<img src="20250216-2.jpg" alt="きのかわ弦楽合奏団のさくらコンサートのちらしの裏面。楽団と会場の紹介"> +<figcaption>Copyright: きのかわ弦楽合奏団</figucaption> +</figure> -<h2>動作環境</h2> +]]></description> +</item> +<item> +<title>謹賀新年</title> +<link>https://www.mtkn.jp/gallery/20250101.html</link> +<guid>https://www.mtkn.jp/gallery/20250101.html</guid> +<pubDate>Wed, 1 Jan 2025 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>謹賀新年</h1> +<time>2025-01-01</time> +<img src="img/20250101.jpg"> +]]></description> +</item> +<item> +<title>麻婆豆腐</title> +<link>https://www.mtkn.jp/kitchen/recipe/mapo_tofu.html</link> +<guid>https://www.mtkn.jp/kitchen/recipe/mapo_tofu.html</guid> +<pubDate>Thu, 10 Oct 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>麻婆豆腐</h1> +<time>2024-10-10</time>作成 +<figure> + <img src="../pics/mapo_tofu.jpg" alt="麻婆豆腐の画像"> + <figcaption>麻婆豆腐</figcaption> +</figure> +<div class="recipe-ingredient"> +<h2>材料(4~5人分)</h2> <ul> -<li>Arch Linux 6.2.12-arch1-1 - <ul> - <li>arm-none-eabi-binutils 2.40-1</li> - <li>GNU Make 4.4.1</li> - </ul> -</li> -<li>OpenBSD 7.3 - <ul> - <li>arm-none-eabi-binutils 2.31.1</li> - <li>make (バージョン?)</li> - </ul> -※<code>make flash</code>は動かん。<code>dmesg</code>でデバイス確認して手動でマウントする必要がある。 -</li> + <li>豆腐: 2丁</li> + <li>ひき肉: 300g</li> + <li>長ネギ: 1本</li> + <li>にんにく: 適量</li> + <li>生姜: 適量</li> + <li>八角: 適量</li> + <li>花椒: 好きなだけ</li> + <li>豆板醤: 好きなだけ</li> + <li>味噌: 適量</li> + <li>酒: 適量</li> + <li>濃口醤油: 適量</li> + <li>塩: 少々</li> + <li>炒め油: 多め</li> </ul> +</div> -<h2>Boot Process</h2> -<p> -RP2040は電源を入れるといくつかの段階(ここでは関係ないので省略。データシート「2.8.1 Processor Controlled Boot Sequence」に詳しく書いてある)を踏んだあと、外部のフラッシュROMの先頭から256バイトを内部のSRAMにコピーして、フラッシュにプログラムが書き込まれているかどうか確認する。RP2040はフラッシュの先頭252バイトから計算したCRC32チェックサムを、直後の253バイト目から256バイトに記録することになっている。起動時にこのチェックサムを確認することで、フラッシュにプログラムが書き込まれているかどうか確かめている。コピーした最後の4バイトと起動時に最初の252バイトから計算したチェックサムが一致していれば、そのままコピーしてきた256バイトの先頭にPCをセットして実行を開始する。一致しなければUSBデバイスモードに切り替わり、パソコンに接続するとストレージとして認識される。このストレージにUF2という形式に変換したプログラムをコピーするとプログラムがフラッシュROMやSRAMに書き込まれる。 -</p> -<p> -以上のことから、プログラムを実行するためにはCRC32を計算し、UF2という形式に変換することが必要である。ソースコードからの流れは以下の通り: -</p> -<pre>source bin bin with -code ----------&gt; object ------&gt; elf --------&gt; bin -------&gt; with --------&gt; crc32 in - crc32 uf2 format - assemble link objcopy bincrc bin2uf2 -</pre> +<div class="recipe-steps"> +<h2>手順</h2> +<ol> + <li>豆腐を水切りしてサイコロに切る</li> + <li>長ネギ、にんにく、生姜を刻む</li> + <li>フライパンに油をしき八角、にんにく、生姜を入れ弱火で香りを出す</li> + <li>火を強めてひき肉を入れ、塩を少しふる</li> + <li>ひき肉に色がついたらネギを入れて炒める</li> + <li>酒、濃口醤油、味噌、豆板醤で味をつける</li> + <li>水を入れてひと煮立ちさせる</li> + <li>花椒、豆腐を入れる</li> + <li>好みで、ラー油、唐辛子、山椒を好きなだけ入れる</li> +</ol> +</div> -<h2>CRC(巡回冗長検査)</h2> -<p> -入力のデータをごにょごにょしてある値を出力する。</p> -<blockquote cite="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB"> -<p> -データ転送等に伴う偶発的な誤りの検査によく使われている<sup>[5]</sup>。 -</p> -</blockquote> -<p> -らしい。 -</p> -<p> -入力のビットを一列に並べて、除数で「割り算」していく。この「割り算」が多項式の除算に似ているので、この除数をCRC多項式というらしい。ただし多項式の除算と違い、引き算するところをXORする。CRC32の場合、除数は33ビットである。33ビットで割ると32ビットの余りが残る。この余りがCRC32のチェックサムである。除数は色々あるようだが、標準的なものがWikipedia<sup>[5]</sup>に列挙されている。除数<code>1011</code>を使ったCRC3の計算の手順は以下の通り: -</p> -<pre><code>1110101011011100110101101101111 入力(適当) -1011 除数(4ビット) -------------------------------- - 101101011011100110101101101111 結果(入力と除数のXOR) - 1011 - ------------------------------ - 00001011011100110101101101111 - 1011 - ------------------------- - 000011100110101101101111 - 1011 - -------------------- - 1010110101101101111 - 1011 - ------------------- - 001110101101101111 - 1011 - ---------------- - 101101101101111 - 1011 - --------------- - 00001101101111 - 1011 - ---------- - 110101111 - 1011 - --------- - 11001111 - 1011 - -------- - 1111111 - 1011 - ------- - 100111 - 1011 - ------ - 01011 - 1011 - ---- - 000 CRC3チェックサム -</code></pre> -<p> -普通の割り算と基本は同じであるが、引き算の部分だけXORになっている。</p> -<p> -以上の計算をプログラムの先頭252バイトに対して、33ビットの除数を用いて行う。データの並べ方は、上の例において左側を先頭に、フラッシュROM上の0番地から、各バイトは最上位ビットから順に並べる。入力のデータは253バイト目から256バイト目に<code>0</code>をひっつけて計算する。これは多分予め長さが分からないデータでも計算できるようにしたかったからかな。除数は<code>0x104c11db7</code>である(最上位ビットは常に1なのでデータシートでは省略されている)。</p> -<p> -入力データは1バイトづつ処理したいみたいである。多分通信等で使う都合である。この時XORは結合則が成り立つので1バイト処理した結果と次のバイトとをXORして次の処理の入力として利用することができる: -</p> -<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当) -|......| -111000110000000000000000000000000 先頭1バイト -100000100110000010001110110110111 除数 ------------------------------------------------------------------------- -011000010110000010001110110110111 - 100000100110000010001110110110111 - ----------------------------------------------------------------------- - 010000001010000110010011011011001 - 100000100110000010001110110110111 - ---------------------------------------------------------------------- - 000000110010001110101000000000101 -|......| - 110010001110101000000000101000000 1バイト目の結果 - |......| - 10000001 入力の2バイト目 - ---------------------------------------------------------------- - 010010011110101000000000101000000 1バイト目の結果と2バイト目のXOR - 100000100110000010001110110110111 除数 - ---------------------------------------------------------------- - 000100011011010010001111100110111 - . - . - . -</code></pre> -<p> -以上の操作は以下のようなアルゴリズムのループで実装できる。</p> -<ul> -<li>前回の結果と、入力データの次のバイトをXOR</li> -<li> - <ul> - <li>先頭の1ビットが1の場合、除数とXORを取り左シフト</li> - <li>先頭の1ビットが0の場合、そのまま左シフト</li> - </ul> -</li> -</ul> -<p> -これを<code>for</code>ループで回す都合上、最初のバイトもXORを取る。上の例では最初は<code>0x0</code>とXORを取っているが、この値を<code>0x0</code>以外にすることもできる。そうした方がいろいろいいこともあるらしい。RP2040では<code>0xffffffff</code>を使う。更にこの工程を32ビットの<code>int</code>だけで行うことを考える: -</p> -<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当) - -11111111111111111111111111111111 0xffffffff -11100011000000000000000000000000 先頭1バイトを24ビットシフト --------------------------------- XOR -00011100111111111111111111111111 -先頭1ビットが0なので1ビットシフト --------------------------------- シフト -00111001111111111111111111111110 -先頭1ビットが0なので1ビットシフト --------------------------------- シフト -01110011111111111111111111111100 -先頭1ビットが0なので1ビットシフト --------------------------------- シフト -11100111111111111111111111111000 -先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR: -11001111111111111111111111110000 シフト -00000100110000010001110110110111 除数の下位32ビット --------------------------------- XOR -11001011001111101110001001000111 -先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR: -10010110011111011100010010001110 シフト -00000100110000010001110110110111 除数の下位32ビット --------------------------------- XOR -10010010101111001101100100111001 -先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR: -00100101011110011011001001110010 シフト -00000100110000010001110110110111 除数の下位32ビット --------------------------------- XOR -00100001101110001010111111000101 -先頭1ビットが0なので1ビットシフト --------------------------------- シフト -01000011011100010101111110001010 -先頭1ビットが0なので1ビットシフト --------------------------------- シフト -10000110111000101011111100010100 1バイト目の結果 +<h2>ひとこと</h2> +<p>我流の適当なレシピなので本場のものが良ければ陳建一さんのでも調べてほしい。</p> +<p>酒はできれば紹興酒のほうがいいと思う。味噌と一緒に豆鼓も入れていいかも。一般的にはもっと旨味を出すために中華だしやら味の素やらを入れると思うが、シンプルに辛いほうが好きなのでそういうものは入れない。陳さんは生姜を入れていなかったが僕はあっていいと思う。豆腐は塩水で茹でていたがこれは真似してもよさそう。</p> +]]></description> +</item> +<item> +<title>棚作った</title> +<link>https://www.mtkn.jp/journal/posts/20241009.html</link> +<guid>https://www.mtkn.jp/journal/posts/20241009.html</guid> +<pubDate>Wed, 9 Oct 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>棚作った</h1> +<time></time> -10000001 入力の2バイト目 --------------------------------- XOR -00000111111000101011111100010100 -先頭1ビットが0なので1ビットシフト --------------------------------- シフト -00001111110001010111111000101000 -. -. -. -</code></pre> <p> -これを実装したのが以下のコード:</p> -<pre><code>uint32_t -crc32(uint8_t *idata, size_t len) -{ - uint32_t pol = 0x04C11DB7; - uint32_t c = 0xFFFFFFFF; - uint32_t b; +自室のクローゼットが使いにくかったので引っ剥がして新しいのを作った。収納していたのはほとんど本だったのだが、奥行きが深いのに棚板が少ないので前後に並べて置いていたため、どこになんの本があるのか見にくく、取り出しにくかった。あとパソコン置き場としても使っていたが、24インチのモニターが一枚ギリギリ入る程度の幅しかなかったのでこれも拡張したかった。久しぶりに木工もしたかったので木材を買って自作した。杉を基本に、パソコンを置く場所だけ檜にした。<p> - for (int i = 0; i &lt; len; i++) { - b = idata[i] &lt;&lt; 24; - c ^= b; - for (int j = 0; j &lt; 8; j++) { - c = c &gt;&gt; 31 & 1 ? c &lt;&lt; 1 ^ pol : c &lt;&lt; 1; - } - } +<figure> +<img src="20241009-1.jpg" alt="クローゼット跡地と買った木材"> +<figcaption>クローゼット跡地と買った木材</figcaption> +</figure> - return c; -} -</code></pre> -<p> -<code>main()</code>関数では上の<code>crc32()</code>に、<code>idata</code>として入力となるバイナリデータの先頭を、<code>len</code>として<code>252</code>を渡してCRC32を計算させる。その後、出力先のファイルに入力元のデータをコピーしていき、253バイト目から256バイト目だけ、計算したCRC32に置き換える。入力元のこの場所にデータが書き込まれていないかどうかは確かめていない。 -</p> +<figure> +<img src="20241009-2.jpg" alt="パソコン机の部分"> +<figcaption>パソコン机の部分</figcaption> +</figure> -<h2>UF2(USB Flashing Format)</h2> -<p> -Microsoftが開発したフラッシュ書き込み用のファイル形式らしい: -<blockquote cite="https://github.com/microsoft/uf2"> <p> -UF2 is a file format, developed by Microsoft for PXT (also known as -Microsoft MakeCode), that is particularly suitable for flashing microcontrollers -over MSC (Mass Storage Class; aka removable flash drive)<sup>[6]</sup>. -</p> -</blockquote> -<p> -このファイルに変換する上で必要な情報はGitHubのmicrosoft/uf2<sup>[6]</sup>に表として纏められている: -<blockquote cite="https://github.com/microsoft/uf2"> -<table> -<thead><tr> -<th>Offset</th><th>Size</th><th>Value</th> -</tr></thead> -<tbody> -<tr> -<td>0</td> -<td>4</td> -<td>First magic number, <code>0x0A324655</code> (<code>"UF2\n"</code>)</td> -</tr> -<tr> -<td>4</td> -<td>4</td> -<td>Second magic number, <code>0x9E5D5157</code></td> -</tr> -<tr> -<td>8</td> -<td>4</td> -<td>Flags</td> -</tr> -<tr> -<td>12</td> -<td>4</td> -<td>Address in flash where the data should be written</td> -</tr> -<tr> -<td>16</td> -<td>4</td> -<td>Number of bytes used in data (often 256)</td> -</tr> -<tr> -<td>20</td> -<td>4</td> -<td>Sequential block number; starts at 0</td> -</tr> -<tr> -<td>24</td> -<td>4</td> -<td>Total number of blocks in file</td> -</tr> -<tr> -<td>28</td> -<td>4</td> -<td>File size or board family ID or zero</td> -</tr> -<tr> -<td>32</td> -<td>476</td> -<td>Data, padded with zeros</td> -</tr> -<tr> -<td>508</td> -<td>4</td> -<td>Final magic number, <code>0x0AB16F30</code></td> -</tr> -</tbody> -</table> -</blockquote> +パソコン机の天板は白木のままでは寂しいので亜麻仁油を塗ってオイルフィニッシュにしてみた。結構黄色が濃くなった。</p> + +<figure> +<img src="20241009-3.jpg" alt="机のオイルフィニッシュ"> +<figcaption>机のオイルフィニッシュ。下においているのが白木のもの</figcaption> +</figure> <p> -RP2040のデータシート<sup>[3]</sup>「2.8.4.2 UF2 Format Details」を見ると、8バイト目のFlagsは、28バイト目にファミリーIDが書き込まれていることを示す<code>0x00002000</code>、12バイト目は、書き込みを行うフラッシュROMの先頭アドレスである<code>0x10000000</code>に、各ブロックの先頭からの位置を足したもの、16バイト目の、各ブロックのデータサイズは256バイト、28バイト目のファミリーIDは<code>0xe48bff56</code>である。あとは表の通り3つのマジックナンバーをセットし、32バイト目以降にデータを書き込み、20バイト目と24バイト目にブロックの通し番号と総数をそれぞれ書き込めばいい。ブロックの通し番号はデータのついでに書き込めるが、総数はデータを全部さばいた後でないと分からないので、最後全てのブロックにまとめて書き込むようにした。できたのが以下のコード: -</p> -<pre><code>#include &lt;stdio.h&gt; -#include &lt;stdint.h&gt; -#include &lt;stdlib.h&gt; -#include &lt;string.h&gt; +せっかくやるならと思って蟻を切ってみた。適当でも案外ガッチリはまるものである。英語ではdovetailというそうだ。鳩のしっぽという意味である。日本語の蟻よりも鳩のしっぽのほうがしっくりくる気がする。</p> +<figure> +<img src="20241009-4.jpg" alt="蟻"> +<figcaption>蟻</figcaption> +</figure> -size_t -fwrite32l(uint32_t d, FILE *f) -{ - int i; - uint8_t b; - for (i = 0; i &lt; 32; i += 8) { - b = (uint8_t) (d &gt;&gt; i & 0xff); - fwrite(&amp;b, 1, 1, f); - if (ferror(f)) { - fprintf(stderr, "Fwrite32l: write error.\n"); - return 0; - } - } - return 4; -} +<p> +上に本棚をつけて完成。本棚は棚を移動しやすいようにと思い、ネジ止め式の棚受けを買った。めんどくさくなったわけではない。左下においているのはもとのクローゼットにあった引き出しである。</p> -int -main(int argc, char *argv[]) -{ - FILE *src = NULL, *dst = NULL; - size_t sdata = 476; - int retnum = 0; +<figure> +<img src="20241009-5.jpg" alt="完成"> +<figcaption>完成</figcaption> +</figure> - uint32_t mag1 = 0x0A324655; - uint32_t mag2 = 0x9E5D5157; - uint32_t flags = 0x00002000; // familyID present - uint32_t addr = 0x10000000; - uint32_t nbyte = 256; - uint32_t blk = 0; - uint32_t nblk = 0; - uint32_t famid = 0xe48bff56; - uint8_t data[sdata]; - uint32_t mag3 = 0x0AB16F30; +<p> +これを作るにあたり、大阪日本橋の刃物屋で鉋を買ってきた。これまではホームセンターで1000円ほどのものだったが、今回買ったのは8000円の物である。店主が台の仕込みと研ぎをした上で売ってくれた。きれいに切れるので小口面の直角を出すのが楽しくなった。小口を下にして自立するようになったときは結構気持ちいい。専門店での買い物は楽しい。餅は餅屋である。</p> - memset(data, 0, sdata); +]]></description> +</item> +<item> +<title>さくらクレヨン</title> +<link>https://www.mtkn.jp/gallery/20240727.html</link> +<guid>https://www.mtkn.jp/gallery/20240727.html</guid> +<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>さくらクレヨン</h1> +<time>2024-07-27</time> +<img src="img/20240727.jpg"> +]]></description> +</item> +<item> +<title>さくらクレパス</title> +<link>https://www.mtkn.jp/gallery/20240702.html</link> +<guid>https://www.mtkn.jp/gallery/20240702.html</guid> +<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>さくらクレパス</h1> +<time>2024-07-02</time> +<img src="img/20240702.jpg"> +]]></description> +</item> +<item> +<title>さくらクレパス</title> +<link>https://www.mtkn.jp/gallery/20240629.html</link> +<guid>https://www.mtkn.jp/gallery/20240629.html</guid> +<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>さくらクレパス</h1> +<time>2024-06-29</time> +<img src="img/20240629.jpg"> +]]></description> +</item> +<item> +<title>さくらクレパス</title> +<link>https://www.mtkn.jp/gallery/20240628.html</link> +<guid>https://www.mtkn.jp/gallery/20240628.html</guid> +<pubDate>Tue, 30 Jul 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>さくらクレパス</h1> +<time>2024-06-28</time> +<img src="img/20240628.jpg"> +]]></description> +</item> +<item> +<title>きのかわ弦楽合奏団第7回定期演奏会</title> +<link>https://www.mtkn.jp/journal/posts/20240428.html</link> +<guid>https://www.mtkn.jp/journal/posts/20240428.html</guid> +<pubDate>Sun, 28 Apr 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>きのかわ弦楽合奏団第7回定期演奏会</h1> +<time>2024-04-28</time> - if (argc != 3) { - fprintf(stderr, "Usage: %s src dst\n", argv[0]); - exit(1); - } +<p>先日きのかわ弦楽合奏団というアマチュアの団体に入団しました。その定期演奏会が7月にあります。多分出演します。</p> - if ((src = fopen(argv[1], "rb")) == NULL) { - fprintf(stderr, "Could not open %s.\n", argv[1]); - retnum = 1; - goto defer; - } - if ((dst = fopen(argv[2], "wb")) == NULL) { - fprintf(stderr, "Could not open %s.\n", argv[2]); - retnum = 1; - goto defer; - } - - while (!feof(src)) { - fwrite32l(mag1, dst); - fwrite32l(mag2, dst); - fwrite32l(flags, dst); - fwrite32l(addr, dst); - fwrite32l(nbyte, dst); - fwrite32l(blk, dst); - fwrite32l(nblk, dst); // dummy - fwrite32l(famid, dst); +<figure> +<img src="7th_concert.jpeg" alt="きのかわ弦楽合奏団の第七回定期演奏会のポスター。7月14日14:00から、かつらぎ総合文化会館、あじさいホールにて"> +<figcaption>Copyright: きのかわ弦楽合奏団</figcaption> +</figure> - fread(data, 1, nbyte, src); - if (ferror(src)) { - fprintf(stderr, "Read error: %s.\n", argv[1]); - retnum = 1; - goto defer; - } - fwrite(data, 1, sdata, dst); - if (ferror(src)) { - fprintf(stderr, "Write error: %s.\n", argv[2]); - retnum = 1; - goto defer; - } +]]></description> +</item> +<item> +<title>RP2040 SDKなし2 Clock、UART</title> +<link>https://www.mtkn.jp/computer/rp2040_2.html</link> +<guid>https://www.mtkn.jp/computer/rp2040_2.html</guid> +<pubDate>Tue, 27 Feb 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>RP2040 SDKなし2 Clock、UART</h1> +<time>2024-02-22</time><br /> +<time>2024-02-27</time>: リセット解除の間違いを修正。 +<p> +今回はClockとUARTを設定してパソコンに繋ぎ、キーボードからの入力をオウム返しするプログラムを作成する。 +<p> +<p> +前回: <a href="rp2040_1.html">RP2040 SDKなしでLチカ</a><br> +ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex2 +</p> - fwrite32l(mag3, dst); +<h2>動作環境</h2> +<ul> +<li>Void Linux + <ul> + <li>cross-arm-none-eabi-binutils-2.32_2</li> + <li>GNU Make 4.4.1</li> + <li>minicom version 2.7.1</li> + </ul> +</li> +<li><a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a> +</li> +</ul> - addr += nbyte; - blk++; - nblk++; - } +<h2>Clock</h2> - for (int i = 0; i &lt; nblk; i++) { - if (i == 0) - if (fseek(dst, 24, SEEK_SET) &lt; 0) { - fprintf(stderr, "Seek error: %s.\n argv[2]"); - retnum = 1; - goto defer; - } - fwrite32l(nblk, dst); - if (i &lt; nblk - 1) - if(fseek(dst, 512 - 4, SEEK_CUR) &lt; 0){ - fprintf(stderr, "Seek error: %s.\n argv[2]"); - retnum = 1; - goto defer; - } - } - -defer: - if (src) - fclose(src); - if (dst) - fclose(dst); - return retnum; -} +<h3>リング発振回路</h3> +<p>RP2040にはリング発振回路というのが内蔵されている。これは自分の出力を反転させようとするもので、不安定だが高速で消費電力の少ないクロックとして用いられる。RP2040は電源を入れると、このリング発振回路を動作用のクロックとして用いている。この発振回路の周波数は、チップの製造過程での誤差、動作時の電圧、動作温度によって変動するので、正確な周波数が必要な用途には向かない。</p> + +<h3>水晶発振子</h3> +<p>秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、より正確である。</p> + +<h3>PLL</h3> +<p>水晶振動子を入力として、周波数を数倍にしたものを出力するもの。電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」によると、入力周波数を<code>FREF</code>としたときの出力周波数は<code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。これらの変数はそれぞれ設定用のレジスタに値を保存することで変更できる。</p> + +<h2>UART</h2> +<p> +Universal Asynchronous Receiver/Transmitterの略。2本の線だけで通信できる。プロトコルは詳しく知らないが、rp2040がよしなにやってくれる。rp2040では同時に二個まで利用できる。どのGPIOピンを使うかもある程度自由に選べる。どのピンが使えるかはデータシートの「2.19.2. Function Select」に書かれている。今回はGPIO0とGPIO1を使う。パソコンとの接続には、秋月電子通商で売っている<a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a>を使用した。UARTで接続するためのパソコン側のソフトウェアはminicomを使用した。僕の環境ではシリアル変換モジュールをパソコンにUSB接続すると、<code>/dev/ttyUSB0</code>として認識されるので、</p> +<pre><code>$ minicom -D /dev/ttyUSB0 </code></pre> -<p><code>fwrite32l()</code>関数は指定されたファイルに32ビットの整数を下位バイトから順に書き込む関数である。バイトオーダーとかややこしそうなので作っておいたけど必要なのかな?あと名前が気に入らない。</p> <p> -CRC32のチェックサムが書き込まれたバイナリファイルを、このプログラムでUF2に変換し、生成されたファイルをUSBストレージとして接続したRP2040にコピーすればフラッシュROMに書き込まれる。 +とすると接続できる。 </p> -<h2>Flash Second Stage</h2> -<p> -RP2040に電源を投入し、CRC32のチェックが通った後、フラッシュROMからコピーされたプログラムの先頭から実行が開始される。このコピーされた部分で、その後の動作に必要な各種の設定を行うことになる。RP2040のデータシートには、フラッシュROMとSSIコントローラのXIPを設定するようにと書かれている。XIPはExecute in Placeの略で、フラッシュROMの内容をCPUから直接実行するものである。SSIはSynchronous Serial Interfaceの略で、周辺機器と情報のやりとりをする通信方式である。RP2040はチップに内蔵されたこのSSIコントローラを通して、外部のフラッシュROMと通信しているのだが、このコントローラを適切に設定すればフラッシュROMの内容がCPUから直接アクセスできる<code>0x10000000</code>番地以降にマップされる。これによりフラッシュROMから内部のSRAMにデータをコピーすることなく命令を実行できるので、速くて便利だという。 -</p> -<p> -しかしこのSSIコントローラはSynopsysという会社のDW_apb_ssiというIPを使っているようで、データシートのSSIコントローラの章は多分Synopsysの人が書いている。その他の章はRaspberry Pi財団の書いたブリティッシュイングリッシュだが、この部分だけ多分ネイティブじゃない人の書いたいい加減な英語である。誤植も多い。何日かかけて理解しようとしたがよく分からん。不毛なので一旦諦めた。</p> +<h2>main.s</h2> +<h3>初期設定</h3> <p> -RP2040には内部にもROMがあり、はバージョン情報や電源を投入した時の動作、その他便利な関数が書き込まれている。この関数の中に外部のフラッシュROMとSSIコントローラを設定するものも含まれているので、今回はこれを利用した。ただしこの方法だとフラッシュROMとの通信方式がStandard SPIのままなので少し遅いらしい。詳しくはデータシートの「2.3.8. Bootrom Contents」を参照。 +後で見るように、UARTの動作には多分水晶発振子とPLLが必要なので、まずはそれを設定する。起動後、メインのプログラムが読み込まれるまでの<code>boot2</code>は前回と同じものである。<code>main.s</code>ではまず前回と同様に初期スタックポインタとエントリーポイントを定義する: </p> +<pre><code> .section .vectors +vectors: + .word 0x20040000 // initial SP + .word (reset+1) // entry point +</code></pre> <p> -RP2040の内蔵ROMの<code>0x00000018</code>番地に関数を検索するための関数がある。この関数に<code>0x00000014</code>番地の<code>rom_func_table</code>と、各関数に割り当てられた二文字の文字列を渡せば、欲しい関数へのポインタが返ってくる。なお、二文字の文字列はそれぞれASCIIコードで現し、二文字目を8ビットシフトしたものと1文字目のORを取ったものを渡すことになっている。今回欲しい関数はフラッシュROMをXIPに設定するもの(<code>_flash_enter_cmd_xip()</code>)なので、<code>'C', 'X'</code>を渡す。関数のポインタが返ってきて、それを呼び出せばフラッシュROMとSSIはXIPモードになる: -</p> -<pre><code>setup_xip: - ldr r3, rom_base +続いて利用するサブシステムのリセットを解除する。PLLとUARTが追加されている。今回使うUARTはUART0だけである。なお、UARTはclock_periが有効化されるまでリセット状態の解除が完了しないようなので、unreset_chkからは外してある:</p> +<pre><code> .section .text +reset: + // unreset gpio, pll_sys, uart0 + ldr r0, =(1 &lt;&lt; 22 | 1 &lt;&lt; 12 | 1 &lt;&lt; 5) // uart0 | pll_sys | io_bank0 + ldr r3, resets_base + ldr r1, atomic_clr + str r0, [r3, r1] // RESETS: RESET + mov r1, #1 + lsl r1, #22 + bic r0, r1 // uart stays in reset state until clock_peri is enabled +unreset_chk: + ldr r1, [r3, #0x8] // RESETS: RESET_DONE + bic r0, r1 + bne unreset_chk - ldrh r0, [r3, #0x14] // rom_func_table - ldr r1, =('C' | 'X' &lt;&lt; 8) // _flash_enter_cmd_xip() - ldrh r2, [r3, #0x18] // rom_table_lookup - blx r2 - blx r0 /* ... */ -rom_base: - .word 0x00000000 -</code></pre> -<p> -XIPの設定が完了すれば、次はメインのプログラムを実行するための準備である。エントリーポイントの指定、スタックポインタの初期値の設定、ベクターテーブルの設定である。Armのマニュアル<sup>[7]</sup>によると、初期スタックポインタとエントリーポイントはベクターテーブルの<code>0x0</code>バイト目と<code>0x4</code>バイト目に書くことになっている:</p> -<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/Exception-number-definition"> -<table> -<caption> -Table 7.3. Exception numbers -</caption><colgroup><col><col></colgroup><thead><tr><th>Exception number</th><th>Exception</th></tr></thead><tbody><tr><td>1</td><td>Reset</td></tr><tr><td>2</td><td>NMI</td></tr><tr><td>3</td><td>HardFault</td></tr><tr><td>4-10</td><td>Reserved</td></tr><tr><td>11</td><td>SVCall</td></tr><tr><td>12-13</td><td>Reserved</td></tr><tr><td>14</td><td>PendSV</td></tr><tr><td>15</td><td>SysTick, optional</td></tr><tr><td>16</td><td>External Interrupt(0)</td></tr><tr><td>...</td><td>...</td></tr><tr><td>16 + N</td><td>External Interrupt(N)</td></tr></tbody> -</table> -</blockquote> +atomic_clr: + .word 0x00003000 +resets_base: + .word 0x4000c000 +</code></pre> -<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/The-vector-table"> -<table> -<caption> -Table 7.4. Vector table format -</caption><colgroup><col><col></colgroup><thead><tr><th>Word offset in table</th><th>Description, for all pointer address values</th></tr></thead><tbody><tr><td>0</td><td>SP_main. This is the reset value of the Main stack pointer.</td></tr><tr><td>Exception Number</td><td>Exception using that Exception Number</td></tr></tbody> -</table> -</blockquote> +<h3>GPIOの設定</h3> <p> -RP2040のベクターテーブルはM0PLUS: VTOR(<code>0xe0000000 + 0xed08</code>)というレジスタに書き込むことで設定する。このとき、下位8ビットは0にしないといけないので、ベクターテーブルの位置は256バイトでアラインする必要がある。ベクターテーブルの定義は<code>main.s</code>に書き、<code>boot2.s</code>からはラベルを使ってアクセスすることにする。以上をまとめると以下のコードになる:</p> -<pre><code> ldr r0, =vectors - ldr r1, m0plus_vtor - str r0, [r1, #0] // vector table - ldr r1, [r0, #4] // entry point - ldr r0, [r0, #0] // stack pointer - mov sp, r0 - bx r1 +次にGPIOの役割を設定する。前回はLEDを点滅させるためにGPIO25をSIOに設定したが、今回はGPIO0とGPIO1をUART0にする: +</p> +<pre><code> // set gpio functions + ldr r3, io_bank0_base + mov r0, #2 // uart0 + mov r1, #0x4 + str r0, [r3, r1] // IO_BANK0: GPIO0_CTRL + mov r1, #0xc + str r0, [r3, r1] // IO_BANK0: GPIO1_CTRL /* ... */ -m0plus_vtor: - .word 0xe0000000 + 0xed08 +io_bank0_base: + .word 0x40014000 </code></pre> -<p>なお以上のコードは<code>.boot2</code>という名前のセクションにしてある。 -</p> - -<h2>メインのコード(<code>main.s</code>)</h2> -<h3>ベクターテーブル</h3> +<h3>Clockの設定</h3> <p> -上で説明したように、ベクターテーブルのアドレスは256バイトの境界にないといけないが、<code>boot2.s</code>をフラッシュの最初の256バイトに配置しており、<code>main.s</code>はその直後から始まるようにリンクする。そのためメインのコードの最初にベクターテーブルを配置すればいい。ここでは割り込みの処理は考えないので、初期スタックポインタとエントリーポイントだけである。初期スタックポインタはSRAMの最後?(<code>0x20040000</code>)、エントリーポイントはエントリーポイントのラベルを用いて設定した。また、別のファイル(<code>boot2.s</code>)からアクセスしたいので、<code>.global</code>宣言をつけておく: +Clockの設定をする。まずは水晶発振子を起動する。水晶発振子は起動してから周波数が安定するまで少し時間がかかるようで、その間待たないといけない。この時間は1msあれば十分だとデータシートに書いている。この待ち時間はXOSC: STARTUPレジスタに、256サイクル単位で記述する。データシートによると初期のリング発振子は最大で12MHzなので、<code>(12 * 10^6 * 1 * 10^-3) / 256 = 47</code>をこのレジスタにセットする。ところでデータシートではこの計算はリング発振子ではなく水晶発振子の周波数で書かれている。起動直後でまだ使えない水晶発振子の周波数を使うのはなんでやろ。SDKでも<code>pico-sdk/src/rp2_common/hardware_xosc/xosc.c</code>で、 </p> -<pre><code> .global vectors -vectors: - .word 0x20040000 // initial SP - .word (reset+1) +<pre><code>#define STARTUP_DELAY (((((XOSC_MHZ * MHZ) / 1000) + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER) </code></pre> <p> -<code>reset</code>ラベルに<code>1</code>を足しているのはRP2040がThumbモードのみに対応しているからである。ArmのCPUはArmモードとThumbモードがあり、Armモードは32ビットの命令で高機能。Thumbモードは16ビットの命令(一部32ビット)でコンパクトである。どちらのモードでも命令は2の倍数のアドレスに並ぶことになる。そのためジャンブ命令のジャンプ先のアドレスの最下位ビットは常に0である。この最下位ビットはジャンプ先のモードを示す為に利用される。両方のモードに対応したCPUではジャンプ先のアドレスの最下位ビットが0ならArmモード、1ならThumbモードに切り替わる。ブランチ命令のオペランド等は多分アセンブラがいい感じにしてくれるので単にラベルを書けば動く。ベクターテーブルのこの部分は自分で足す必要があるみたい。あんまりちゃんと調べてないのでマニュアル読んでや。</p> - -<h3>GPIOの設定</h3> +と定義されている(PICO_XOSC_STARTUP_DELAY_MULTIPLIERは1)。とりあえず47に設定しているが、試しに0や1にしても動いた。よくわからん。</p> <p> -電源投入直後、RP2040の周辺機器はリセット状態になっている。まずは今回利用するGPIOのリセット状態を解除する必要がある。データシートの「2.14. Subsystem Resets」には以下のように書かれている: -</p> -<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf"> +待ち時間を設定したら発振子を起動する。XOSC: CTRLに起動用のコマンド的なものを入力し、周波数が安定するのを待つ。</p> <p> -Every peripheral reset by the reset controller is held in reset at power-up. -It is up to software to deassert the reset of peripherals it intends to use. +以上を実装したのが以下のコード: </p> -</blockquote> +<pre><code> // setup xosc + ldr r3, xosc_base + mov r0, #47 // start up delay for 12MHz rosc (xosc?) + str r0, [r3, #0xc] // XOSC: STARTUP + ldr r0, =(0xfab &lt;&lt; 12 | 0xaa0) + str r0, [r3, #0] // XOSC: CTRL +wait_xosc: + ldr r0, [r3, #0x4] // XOSC: STATUS + lsr r0, r0, #31 // STABLE bit + beq wait_xosc + +/* ... */ + +xosc_base: + .word 0x40024000 +</code></pre> + +<h3>PLLの設定</h3> <p> -リセット状態を解除するには、RESETS_BASE(<code>0x4000c000</code>)から<code>0x0</code>バイト目のRESETS: RESETレジスタのうち利用したい周辺機器のビットを<code>0x0</code>にすればいい。 -GPIOはIO Bank 0なので(これ明記されてなくない?)、RESETS: RESETレジスタのIO_BANK0(5番ビット)を<code>0x0</code>にする。 -</p> -<h4>レジスタのアトミックなクリア</h4> +水晶発振子が起動できたので、次にPLLを設定する。CPUが133MHzまで対応しているので133MHzになるようにした。</p> <p> -RESETS: RESETレジスタのうち5番ビットだけを<code>0x0</code>にしたい。この時、まずこのレジスタを読み込んでから<code>~(1 &lt;&lt; 5)</code>と論理積を取って同レジスタに書き戻してもいいのだが、RP2040にはこれを一回の<code>str</code>でしかもアトミックにできる機能が用意されている。今回の場合アトミックかどうかは関係ないと思うけど。</p> +PLLは入力となる振動(ここでは水晶発振子の振動)を加工して周波数を上げたり下げたりする。出力の周波数は以下の式で決まる: +</p> +<pre>(FREF / REFDIV) * FBDIV / (POSTDIV1 * POSTDIV2)</pre> <p> -各レジスタには4個のアドレスが割り当てられている。データシートの各章のList of Registersに記載されているアドレスは通常の読み書きができる。そのアドレスに<code>0x1000</code>を足したものにアクセスするとアトミックなXORが、<code>0x2000</code>を足したものはアトミックなセットが、<code>0x3000</code>を足したものはアトミックなクリアができる。つまりレジスタのアドレスに<code>0x3000</code>を足したものに、<code>0x1 &lt;&lt; 5</code>を<code>str</code>すれば5番目のビットだけ<code>0x0</code>にして、他のビットは変更されない。逆に指定したビットだけ立てて他を触らない場合は<code>0x2000</code>を、あるいは指定したビットだけトグルしたい場合は<code>0x1000</code>を足したアドレスにアクセスすればいい。</p> -<h4>リセット状態の確認</h4> -<p>リセットの解除はすぐに完了するわけではないようである。リセットの解除が完了したかどうか確認するにはRESETS: RESET_DONEレジスタ(RESETS_BASEから<code>0x8</code>バイト目)の該当するビット(ここでは5番目のビット)を読む。この値が<code>0x1</code>であればリセットの解除が完了している。<code>0x0</code>であれば処理が進行中なので<code>0x1</code>が返ってくるまで繰り返し読み込んで<code>0x0</code>になるまで待機する。ところでこのレジスタはリセットの解除が完了したかどうか確かめるものなので、RESET_DONEという名前はどうなん? +FREFは入力の周波数(ここでは12MHz)で、その他の変数はプログラマが設定できる。ただしデータシートによると(FREF / REFDIV)は5MHz以上でないといけないので、REFDIVは1である。また、FBDIVは16〜320、POSTDIV1とPOSTDIV2は1〜7で、POSTDIV1とPOSTDIV2に違う値を代入する場合、POSTDIV1に大きい方を入れたほうが消費電力が少なくなるとのことなので、133MHzにするには、FBDIV=133、POSTDIV1=6、POSTDIV=2とすればいい(POSTDIV1=4、POSTDIV2=3も可能だが、pico-sdkに付属するvcocalc.pyというスクリプトのコメントには、この2つの値の差が大きい方がいいと書いている)。 +</p> <p> -以上から、GPIOのリセットを解除するのは以下のコード: +PLL設定の手順は、FBDIVの設定、PLLとVCOの起動、VOCが安定するまで待機、POSTDIV1とPOSTDIV2の設定、Post Dividerの起動、そして最後にシステムとUARTのクロックを今設定したPLLに変更、である。以上を実装したのが以下のコード: </p> -<pre><code>reset: - // unreset gpio - mov r0, #1 - lsl r0, r0, #5 // io_bank0 - ldr r3, resets_base +<pre><code> // setup pll_sys 133MHz + ldr r3, pll_sys_base + // set feedback divider + mov r0, #133 + str r0, [r3, #0x8] // PLL: FBDIV_INT + // power on pll and vco + ldr r0, =(1 &lt;&lt; 5 | 1) // VCOPD | PD ldr r1, atomic_clr - str r0, [r3, r1] // RESETS: RESET -reset_chk: - ldr r1, [r3, #0x8] // RESETS: RESET_DONE - tst r0, r1 - beq reset_chk + add r1, r1, #0x4 + str r0, [r3, r1] // PLL: PWR + // wait vco to lock +wait_vco: + ldr r0, [r3, #0] // PLL: CS + lsl r0, r0, #31 + beq wait_vco + // setup post dividers + ldr r0, =(4 &lt;&lt; 16 | 3 &lt;&lt; 12) + str r0, [r3, #0xc] // PLL: PRIM + // power on post divider + mov r0, #8 // POSTDIVPD + str r0, [r3, r1] // PLL: PWR + + // set system clock clksrc_pll_sys + ldr r3, clocks_base + ldr r0, =(0x0 &lt;&lt; 5 | 0x1) + str r0, [r3, #0x3c] // CLOCKS: CLK_SYS_CTRL + // enable clk_peri + mov r0, #1 + lsl r0, r0, #11 + str r0, [r3, #0x48] // CLOCKS: CLK_PERI_CTRL /* ... */ atomic_clr: .word 0x00003000 -resets_base: - .word 0x4000c000 +clocks_base: + .word 0x40008000 +pll_sys_base: + .word 0x40028000 </code></pre> -<h3>GPIOの機能の選択</h3> -<p>RP2040のGPIOにはそれぞれ複数の機能が用意されていて、どれを使うかはソフトウェアから選択できる。利用できる機能の一覧と各機能の説明はデータシートの「2.19.2 Function Select」に詳しく書いてある。ここではGPIO25番のピンをSIO(Single-cycle IO)として利用する。同じCPUが載っているRaspberry Pi PicoはGPIO25番にLEDが半田付けされている。25番にしたのはこれに合わせるためである。他のピンでもいい。GPIOに1か0を印加するだけならこのSIOを使うみたいである。Single-cycleはCPUから操作したときに1クロックでその操作が完了するという意味らしい(本当か)。SIOの詳しい説明はデータシートの「2.3.1 SIO」にある。</p> +<h3>UARTの設定</h3> <p> -GPIO25番の機能を選択するにはIO_BANK0_BASE(<code>0x40014000</code>)から<code>0xcc</code>番目のGPIO25_CTRLレジスタの下位5ビットに、該当する機能の番号を書き込めばいい。データシートの「2.19.2 Function Select」にある表を見ると、GPIO25番のSIOは5である:</p> -<pre><code> // set gpio functions - ldr r3, io_bank0_base - mov r0, #5 // sio - mov r1, #0xcc - str r0, [r3, r1] // IO_BANK0: GPIO25_CTRL +データシートによるとUART設定の手順は以下の通り: +</p> +<ul> +<li>リセットの解除</li> +<li>clock_periの設定</li> +<li>UARTの有効化</li> +<li>FIFOの有効化</li> +<li>転送速度の設定</li> +<li>フォーマットの設定</li> +</ul> +<p> +上の2つは既に終えている。残りの部分はこの順番どおりに設定しても動かなかった。C言語で書かれたサンプルを見ると、クロックを設定した後、転送速度の設定、UARTの有効化、FIFOの有効化の順になっている。そのとおりにすると動いた。理由はよく理解していないが、変数を設定してから起動するほうが素直ではある。</p> +<p> +転送速度はminicomのデフォルトである115200 baudに設定する。データシート「4.2.7.1. Baud Rate Calculation」の計算式において、クロック周波数を125MHzから133MHzに変えて計算して、BRDI=72、BDRF=0.157(=10/64)となる。この数値をUART: UARTIBRD、UART: UARTFBRDレジスタにそれぞれ代入する。 +</p> +<p> +UARTの有効化はUART: UARTCRレジスタのUARTENビットをセットすることで行う。C言語のサンプルでは同じレジスタのRXE、TXEビットもセットしているが、この2つはもともと1になっているのでほっといてよさそう。</p> +<p> +FIFOの有効化はUART: UARTLCR_HレジスタのFENビットをセットすることで行う。また、同じレジスタの他のビットで、データーのフォーマットを設定できる。ここではminicomのデフォルトに合わせてWLENを8bitにする。</p> +<p> +以上をまとめると以下のようになる: +</p> +<pre><code> // setup uart0 + ldr r3, uart0_base + // set baudrate 115200 + // BDRI = 72, BDRF = 0.157 (10 / 64) + mov r0, #72 + str r0, [r3, #0x24] // UART: UARTIBRD + mov r0, #10 + str r0, [r3, #0x28] // UART: UARTFBRD + // enable uart0 + mov r0, #1 // UARTEN + ldr r1, atomic_set + add r1, r1, #0x30 + str r0, [r3, r1] // UART: UARTCR + // enable FIFO and set format + ldr r0, =(3 &lt;&lt; 5 | 1 &lt;&lt; 4) // WLEN = 8, FEN = 1 + str r0, [r3, #0x2c] // UART: UARTLCR_H /* ... */ -io_bank0_base: - .word 0x40014000 +atomic_set: + .word 0x00002000 +uart0_base: + .word 0x40034000 </code></pre> -<h3>GPIOの出力を有効化</h3> +<h3>UARTの入出力</h3> <p> -GPIO25番がSIOになったので、次にこのピンからの出力を有効化する。既定値では出力は無効になっている。ハイインピーダンスってことなのかな?出力を有効にするには、SIO_BASE(<code>0xd0000000</code>)から<code>0x24</code>バイト目のSIO: GPIO_OEレジスタの該当するビット(25番のピンなので25番ビット)を<code>0x1</code>にする: +設定が終わったので実際にUARTの入出力を処理するコードを書く。まずUARTからの出力は、出力したいバイトをUART: UARTDRに書き込むことで行う。その際、書き込まれたデータは一時的に出力用FIFOに保持されるので、このFIFOが満杯でないことを確認する必要がある。FIFOの状態はUART: UARTFRレジスタで確認できる。このレジスタのTXFFの値が1であればデータを書き込めないので、0になるまで待機する。関数名は<code>putbyte</code>にした。また出力したいデータは<code>r0</code>レジスタにの下位8ビットに入れられているものとした。書き込めるデーターは8ビットだけなので、<code>0xff</code>と論理積をとってから書き込んでいる: </p> -<pre><code> // enable gpio output - ldr r3, sio_base - mov r0, #1 - lsl r0, r0, #25 // gpio25 - str r0, [r3, #0x24] // SIO: GPIO_OE +<pre><code>putbyte: + ldr r3, uart0_base + mov r1, #1 + lsl r1, r1, #5 // TXFF +txff: + ldr r2, [r3, #0x18] // UART: UARTFR + tst r1, r2 + bne txff + mov r1, #0xff + and r0, r0, r1 + str r0, [r3, #0] // UART: UARTDR + bx lr /* ... */ -sio_base: - .word 0xd0000000 +uart0_base: + .word 0x40034000 </code></pre> -<h3>LEDの点滅</h3> -<p>以上でGPIOの設定は完了したので、あとは実際にLEDに電圧を掛けるだけである。レジスタのアドレスに<code>0x1000</code>を足したものに書き込むとアトミックなレジスタのXORができると書いたが、SIOはこの機能がサポートされていないようである。データシートの「2.1.2 Atomic Register Access」に、 -</p> -<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf"> -<p> -The SIO (Section 2.3.1), a single-cycle IO block attached directly to the cores' -IO ports, does <strong>not</strong> support atomic accesses at the bus level, -although some individual registers (e.g. GPIO) have set/clear/xor aliases. -</p> -</blockquote> <p> -と書かれている。そのかわりここにも書かれている通り、SIOの一部のレジスタにはアトミックなセット/クリア/XORをするためのレジスタが用意されている。ここではLEDを点滅させるためにGPIOの出力をトグルしたいのでXOR用のレジスタを使う。SIO_BASE(<code>0xd0000000</code>)から<code>0x1c</code>バイト目のSIO: GPIO_OUT_XORレジスタがそれである。このレジスタの25番ビットに<code>0x1</code>を書き込めばいい。出力をトグルした後は少し間をおいて同じことを繰り返す。間をおくためにここでは適当な数値を1づつ減らしていって0になったら返る関数<code>delay</code>を作った。タイマーと割り込みを使ったほうが消費電力等で優位なようだが、面倒なのでとりあえずこれで:</p> - -<pre><code> // blink led on gpio25 - ldr r4, sio_base - mov r5, r0 // r0 = 1 &lt;&lt; 25 -loop: - str r5, [r4, #0x1c] // SIO: GPIO_OUT_XOR - bl delay - b loop - -delay: - mov r0, #1 - lsl r0, r0, #20 -delay_loop: - sub r0, r0, #1 - bne delay_loop +入力はUART: UARTDRの下位8ビットを読むことで得られる。UARTからの入力は、一時的に入力用FIFOに保存される。このFIFOが空の状態でデータを読んでも意味がないので、FIFOが空でないことを確認する必要がある。これはUART: UARTFRレジスタのRXFEを読むことで確認できる。本来は入力があったときに割り込みを発生させて、それまではCPUを休ませるか別の処理をさせておくべきだが、とりあえずここではループでFIFOの状態を確認し続けている。関数名は<code>getbyte</code>にした。 +読み込んだデータは<code>r0</code>レジスタに保存している:</p> +<pre><code>getbyte: + ldr r3, uart0_base + mov r1, #1 + lsl r1, r1, #4 // RXFE +rxfe: + ldr r2, [r3, #0x18] // UART: UARTFR + tst r1, r2 + bne rxfe + ldr r0, [r3, #0] // UART: UARTDR + mov r1, #0xff + and r0, r0, r1 bx lr /* ... */ -sio_base: - .word 0xd0000000 +uart0_base: + .word 0x40034000 </code></pre> -<p>なお以上のコードは<code>.text</code>セクションである。</p> - -<h2>リンカスクリプト</h2> <p> -以上のコードには<code>.boot2</code>、<code>.text</code>の2つのセクションが含まれる。<code>.boot2</code>はフラッシュの先頭から256(<code>0x100</code>)バイト目まで、<code>.text</code>はその後ろに続くように配置する: -<pre><code>MEMORY -{ - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k -} +あとはこの2つの関数をループの中で交互に呼び出せば、オウム返しするだけのプログラムが完成する: +</p> +<pre><code>loop: + bl getbyte + bl putbyte + b loop +</code></pre> -SECTIONS -{ - .boot2 : { - *(.boot2) - . = 0x100; - } > FLASH +<h2>リング発振回路でUARTは動くんかな?</h2> +<p>UARTの通信には正確なクロックが必要である。その為上では<code>clk_peri</code>として水晶発振子とPLLを用いた。ところがpico-examplesのhello_uartでは<code>main()</code>関数で水晶発振子を設定していない。そこでリング発振回路を用いてみたのだが、どうもうまく通信できない。出力されている正確な周波数も分からないのであきらめることにした。オシロスコープなんていうものは持っていない。</p> - .text : { - *(.text) - } > FLASH -} +<h3>pico-sdk</h3> +<p> +ところがどうも調べているとSDKを使った場合、デフォルトではクロック周波数は125MHzになっているらしい。どうやら水晶発振子もPLLも<code>main()</code>が呼ばれる前に設定されているようである。</p> +<p> +pico-examplesのサンプルプログラムはビルドすると自動で逆アセンブリしたファイルを出力してくれる。これを見ると、最初の256バイトは前回説明したboot2のコードで、その後ろにベクターテーブルが続く。ベクターテーブルの最初は初期スタックポインタで、<code>0x20042000</code>になっている。次はエントリーポイントで、<code>0x100001f7</code>である:</p> +<pre><code>10000100 &lt;__VECTOR_TABLE&gt;: +10000100: 20042000 .word 0x20042000 +10000104: 100001f7 .word 0x100001f7 </code></pre> - -<h2>Makefile</h2> <p> -以上のソースコードは以下のように配置している: +Thumbモードなので実際のエントリーポイントは<code>1</code>引いた、<code>0x100001f6</code>である。この場所ではまず自分のCPUIDを調べて、<code>1</code>であれば待機状態に移行する。RP2040はデュアルコアである。起動直後はCPUIDが<code>0</code>のコアだけで処理をして、CPUIDが<code>1</code>のコアはプログラマが必要に応じて起動することになっている。このためCPUIDが<code>1</code>のコアは起動してすぐに待機状態に入ることがデータシートに書かれている。しかしこの処理はユーザーの書いたプログラムじゃなくて内蔵ROMにある起動用プログラムが担当するみたいに書かれてるんやけど、なんでSDKではユーザープログラムの一部として組み込んでるんかな? </p> -<pre>rp2040 -├── ex1 -│   ├── Makefile -│   ├── boot2.s -│   ├── main.s -│   └── memmap.ld -└── tools - ├── Makefile - ├── bin2uf2.c - └── bincrc.c -</pre> -<p> -toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。ex1ディレクトリのMakefileは以下の通り: +<pre><code>100001f6 &lt;_reset_handler&gt;: +100001f6: 481d ldr r0, [pc, #116] ; (1000026c &lt;hold_non_core0_in_bootrom+0xe&gt;) +100001f8: 6800 ldr r0, [r0, #0] +100001fa: 2800 cmp r0, #0 +100001fc: d12f bne.n 1000025e &lt;hold_non_core0_in_bootrom&gt; +</code></pre> +<p>上のコードの最初の<code>ldr</code>は、<code>0xd0000000</code>(M0PLUS: CPUIDレジスタ)をロードしている。最後の飛び先<code>0x1000025e</code>はCPUIDが<code>1</code>のCPUを待機させる処理である:</p> +<pre><code>1000025e &lt;hold_non_core0_in_bootrom&gt;: +1000025e: 4809 ldr r0, [pc, #36] ; (10000284 &lt;hold_non_core0_in_bootrom+0x26&gt;) +10000260: f001 fb9c bl 1000199c &lt;rom_func_lookup&gt; +10000264: 4700 bx r0 +10000266: 0000 .short 0x0000 +/* ... */ +10000284: 00005657 .word 0x00005657 +</code></pre> +<p>内蔵フラッシュに書きこまれた関数を呼びだしている。呼びだしに使うコードは<code>0x00005657</code>(<code>'W' | 'V' &lt;&lt; 8</code>)である。データシートを見ると、この関数は<code>_wait_for_vector()</code>という名前で、CPUIDが1のCPUを寝かしつけるのに使われると書いている。この部分のソースコードをpico-sdkで探すと<code>pico-sdk/src/rp2_common/pico_standard_link/crt0.S</code>というのが見付かった:</p> +<pre><code>$ find pico-sdk/src -type f | xargs grep -l _reset_handler +pico-sdk/src/rp2_common/pico_standard_link/crt0.S +</code></pre> +<p>このファイルによると: </p> -<pre><code>AS = arm-none-eabi-as -LD = arm-none-eabi-ld -OBJCOPY = arm-none-eabi-objcopy -BINCRC = ../tools/bincrc -BIN2UF2 = ../tools/bin2uf2 - -MCPU = -mcpu=cortex-m0plus -ASFLAGS = $(MCPU) -CFLAGS = $(MCPU) -ffreestanding -nostartfiles -O0 -fpic -mthumb -c -LDFLAGS = --no-relax -nostdlib - -all: tools led.uf2 - -clean: - rm -f *.o *.elf *.uf2 *.bin - cd ../tools &amp;&amp; make clean - -.s.o: - $(AS) $(ASFLAGS) -o $@ $&lt; - -led.elf: boot2.o main.o memmap.ld - $(LD) $(LDFLAGS) -o $@ -T memmap.ld boot2.o main.o - -led.bin: led.elf - $(OBJCOPY) -O binary led.elf $@ +<pre><code> // Only core 0 should run the C runtime startup code; core 1 is normally + // sleeping in the bootrom at this point but check to be sure +</code></pre> +<p>だそうである。やっぱり無駄やん。内蔵フラッシュのプログラムにバグがあってもこのコードのせいで見付かりにくくなってない?知らんけど。</p> -led.uf2: led.bin - $(BINCRC) led.bin led_crc.bin - $(BIN2UF2) led_crc.bin $@ +<p>続いて<code>.data</code>領域と<code>.bss</code>領域のコピー、初期化のようである。多分OSの本かなんかで習ったメモリマップの話:</p> +<pre><code>100001fe: a40d add r4, pc, #52 ; (adr r4, 10000234 &lt;data_cpy_table&gt;) +10000200: cc0e ldmia r4!, {r1, r2, r3} +10000202: 2900 cmp r1, #0 +10000204: d002 beq.n 1000020c &lt;_reset_handler+0x16&gt; +10000206: f000 f812 bl 1000022e &lt;data_cpy&gt; +1000020a: e7f9 b.n 10000200 &lt;_reset_handler+0xa&gt; +1000020c: 4918 ldr r1, [pc, #96] ; (10000270 &lt;hold_non_core0_in_bootrom+0x12&gt;) +1000020e: 4a19 ldr r2, [pc, #100] ; (10000274 &lt;hold_non_core0_in_bootrom+0x16&gt;) +10000210: 2000 movs r0, #0 +10000212: e000 b.n 10000216 &lt;bss_fill_test&gt; -flash: all - mount /dev/disk/by-label/RPI-RP2 /mnt - cp led.uf2 /mnt +10000214 &lt;bss_fill_loop&gt;: +10000214: c101 stmia r1!, {r0} -tools: - cd ../tools &amp;&amp; make +10000216 &lt;bss_fill_test&gt;: +10000216: 4291 cmp r1, r2 +10000218: d1fc bne.n 10000214 &lt;bss_fill_loop&gt; </code></pre> -<p> -RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し、ex1ディレクトリで</p> -<pre><code>$ make -# make flash +<p>最後にいろいろ呼びだす:</p> +<pre><code>1000021a &lt;platform_entry&gt;: +1000021a: 4917 ldr r1, [pc, #92] ; (10000278 &lt;hold_non_core0_in_bootrom+0x1a&gt;) +1000021c: 4788 blx r1 +1000021e: 4917 ldr r1, [pc, #92] ; (1000027c &lt;hold_non_core0_in_bootrom+0x1e&gt;) +10000220: 4788 blx r1 +10000222: 4917 ldr r1, [pc, #92] ; (10000280 &lt;hold_non_core0_in_bootrom+0x22&gt;) +10000224: 4788 blx r1 +10000226: be00 bkpt 0x0000 +10000228: e7fd b.n 10000226 &lt;platform_entry+0xc&gt; +/* ... */ +10000278: 10001819 .word 0x10001819 +1000027c: 100002dd .word 0x100002dd +10000280: 10001909 .word 0x10001909 </code></pre> -<p> -とすればプログラムがRP2040のボードに書き込まれて実行が開始される。</p> +<p>一つめの<code>blx</code>は<code>0x10001818</code>(<code>runtime_init</code>)を、二つめは<code>0x100002dc</code>(<code>main</code>)を、最後のは<code>0x10001908</code>(<code>exit</code>)を、それぞれ呼んでいる。この<code>runtime_init</code>はアセンブリでは分かりにくいのでソースコードを探してみると、以下のものが見付かった:</p> +<pre><code>$ find pico-sdk/src -type f | xargs grep -l runtime_init +pico-sdk/src/rp2_common/pico_runtime/runtime.c +pico-sdk/src/rp2_common/pico_standard_link/crt0.S +pico-sdk/src/common/pico_sync/include/pico/mutex.h +</code></pre> +<p>最後の<code>mutex.h</code>は関係なさそう。二つめの<code>crt0.S</code>は呼びだしてるだけ。一つめの<code>runtime.c</code>が多分探しているものである。これを見るとまず各種周辺機器を一度リセットし、リセット状態を解除している。使わんやつも初期化してない?その後<code>clocks_init()</code>を呼んでいる。この関数は<code>pico-sdk/src/rp2_common/hardware_clocks/clocks.c</code>で定義されている。これを見ると、<code>xosc_init()</code>を呼んで水晶発振子を初期化した後、<code>clk_peri</code>を125MHzに設定している:</p> +<pre><code> clock_configure(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, + 125 * MHZ, + 125 * MHZ); +</code></pre> +<p>やっぱり水晶発振子じゃないとあかんのかな。</p> + +<h2>CMake</h2> +<p>上ではビルドしたバイナリを逆アッセンブルして読んだ。わざわざこんなことをしなくてもMakefile読めばなにがどうなって最終生成物に辿りつくのか分かればいいのだが、そうもいかない。このSDKとpico-examplesにはビルドシステムとしてCMakeなるものが使われている。これがどうも複雑でよく分からない。勉強する気にもならん。上で見た<code>crt0.S</code>や<code>runtime.c</code>といったファイルも<code>hello_uart</code>で本当に使われているものなのかもよく分からない。こんな煩雑なものは本当に必要なのかな。無駄に複雑にしてるだけとちゃうんかな。特に僕は勉強用に使ってるので、ソースコードの依存関係をもっと分かりやすくしてくれないと、内部でなにがどうなってるのか理解しにくい。何度か頑張って読もうとしたが、面白くないのでやめた。数百行のファイルをあっちからこっちから<code>include</code>してるし、大文字ばかりの変数だらけで目が痛い。こんなものを扱えるというのはえらいええ頭してはるんやね。</p> -<h2>最後に</h2> -<p> -光あれ。 -</p> <h2>参考</h2> <ul> <li> -[1] Hennesy, J. L. and Patterson, D. A. 2017. Computer Organization And Design RISC-V Edition. -</li> -<li> -[2] <a href="https://akizukidenshi.com/catalog/g/gK-17542/">RP2040マイコンボードキット.秋月電子通商</a> -</li> -<li> -[3] <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> +<a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> </li> <li> -[4] <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> +<a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> </li> <li> -[5] <a href="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB">巡回冗長検査.Wikipedia</a> +<a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> </li> <li> -[6] <a href="https://github.com/microsoft/uf2">USB Flashing Format (UF2).GitHub</a> +<a href="https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%82%B7%E3%83%AC%E3%83%BC%E3%82%BF">リング・オシレータ.Wikipedia</a> </li> <li> -[7] <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> +<a href="https://www5.epsondevice.com/ja/information/technical_info/osc.html">水晶発振器とは? 原理と仕組み、水晶振動子との違い、選び方のポイントを解説.エプソン水晶デバイス</a> </li> </ul> ]]></description> </item> <item> -<title>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</title> -<link>https://www.mtkn.jp/computer/mailserver.html</link> -<guid>https://www.mtkn.jp/computer/mailserver.html</guid> -<pubDate>Sat, 24 Feb 2024 00:00:00 +0900</pubDate> -<description><![CDATA[<h1>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</h1> -<time>2024-01-19</time> +<title>RP2040 SDKなしでLチカ</title> +<link>https://www.mtkn.jp/computer/rp2040_1.html</link> +<guid>https://www.mtkn.jp/computer/rp2040_1.html</guid> +<pubDate>Sun, 25 Feb 2024 00:00:00 +0900</pubDate> +<description><![CDATA[<h1>RP2040 SDKなしでLチカ</h1> +<time>2023-04-25</time>: 作成<br /> +<time>2024-02-25</time>: ベクターテーブルの修正 <h2>はじめに</h2> <p> -少し前にさくらのVPSで独自ドメインのメールサーバーを構築した。当時はドキュメントを追うのに必死で構築手順を整理できていなかった。しかしスマホを使わない僕の数少い連絡手段なので、受信できなくなると困る。そこであらためてサーバーを構築し、その手順を確認することにした。あいかわらずDNSの知識は自信がない。セキュリティは自己責任で。</p> +パタヘネのRISC-V<sup>[1]</sup>版を買って一通り読んだらアセンブリ言語で組込のプログラミングがしたくなった。RISC-Vのマイコンボードが欲しかったのだが、安くていい感じのものが見付からなかった。代わりに秋月電子通商でArmのものがあった。RP2040マイコンボードキット<sup>[2]</sup>というものである。ウェブ上の情報も多く、データシート<sup>[3]</sup>もしっかりしていそうなので、とりあえずこれを買ってみた。</p> +<p> +一般的にはSDK<sup>[4]</sup>をダウンロードしてあらかじめ用意されたライブラリを使って開発するようだが、これはビルドシステムとしてcmakeというのを使っている。これがOpenBSDでは何かエラーがでて動かなかった。僕はこういう便利ツールが嫌いだ。どうせ使わんからいいんやけど。関係ないけど途中から開発環境がLinuxに替わった。SDKには便利な関数がたくさん用意されているので楽である。ハードウェアの面倒な部分がプログラマから見えないようにしているからである。しかし今回はその面倒な部分に触れてみたくて買ったので、SDKを使うと意味がない。</p> +<p> +ということでSDKなしで開発してみる。とりあえず定番のLチカをば。</p> +<p> +ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a> +</p> -<h2>使うもの</h2> +<h2>動作環境</h2> <ul> -<li>VPS: さくらのVPS (IPアドレスを<i>sss.sss.sss.sss</i>とする)</li> -<li>OS: OpenBSD 7.4</li> -<li>ドメイン: ムームードメイン (mtkn.jpとする)</li> -<li>SMTPサーバー(MTA): OpenSMTPD 7.4.0</li> -<li>IMAPサーバー(MDA): Dovecot 2.3.20</li> +<li>Arch Linux 6.2.12-arch1-1 + <ul> + <li>arm-none-eabi-binutils 2.40-1</li> + <li>GNU Make 4.4.1</li> + </ul> +</li> +<li>OpenBSD 7.3 + <ul> + <li>arm-none-eabi-binutils 2.31.1</li> + <li>make (バージョン?)</li> + </ul> +※<code>make flash</code>は動かん。<code>dmesg</code>でデバイス確認して手動でマウントする必要がある。 +</li> </ul> -<h2>メールの仕組み</h2> -<p> -メールの送受信にはSMTPというプロトコルが使われ、この通信を担うのがSMTPサーバーと呼ばれるソフトウェアである。このサーバーはメールの転送を担うので、MTA(Mail Transfer Agent)とも呼ばれる。今回はMTAとしてOpenSMTPDというのを使う。手元のパソコン等で書いたメールは、SMTPの通信で送信側のMTAに送られ、そこから受信側のMTAに転送される。受信側のMTAでは届いたメールをユーザーごとにふりわけ、所定の場所に保存する。</p> +<h2>Boot Process</h2> <p> -受信側のユーザーは、手元のパソコン等から受信側のサーバーに繋ぎ、自分宛てのメールを確認する。この時に使われるのがIMAPというプロトコルである(POPというのもあるが今回は割愛)。IMAPによるこの通信を担うのがIMAPサーバーで、メールを配達する役割を担うことから、MDA(Mail Delivery Agent)とも呼ばれる。今回MDAとしてDovecotを使用する。</p> +RP2040は電源を入れるといくつかの段階(ここでは関係ないので省略。データシート「2.8.1 Processor Controlled Boot Sequence」に詳しく書いてある)を踏んだあと、外部のフラッシュROMの先頭から256バイトを内部のSRAMにコピーして、フラッシュにプログラムが書き込まれているかどうか確認する。RP2040はフラッシュの先頭252バイトから計算したCRC32チェックサムを、直後の253バイト目から256バイトに記録することになっている。起動時にこのチェックサムを確認することで、フラッシュにプログラムが書き込まれているかどうか確かめている。コピーした最後の4バイトと起動時に最初の252バイトから計算したチェックサムが一致していれば、そのままコピーしてきた256バイトの先頭にPCをセットして実行を開始する。一致しなければUSBデバイスモードに切り替わり、パソコンに接続するとストレージとして認識される。このストレージにUF2という形式に変換したプログラムをコピーするとプログラムがフラッシュROMやSRAMに書き込まれる。 +</p> <p> -以上をまとめると以下のようになる: +以上のことから、プログラムを実行するためにはCRC32を計算し、UF2という形式に変換することが必要である。ソースコードからの流れは以下の通り: </p> -<pre> -+------------+ +----------------+ -|MTA(mtkn.jp)|--SMTP-&gt;| MTA | -+------------+ +--example.com---+ - ^ | MDA | - | +----------------+ - | | - SMTP IMAP - | | - | v -+------------+ +----------------+ -|user@mtkn.jp| |user@example.com| -+------------+ +----------------+ - +<pre>source bin bin with +code ----------&gt; object ------&gt; elf --------&gt; bin -------&gt; with --------&gt; crc32 in + crc32 uf2 format + assemble link objcopy bincrc bin2uf2 </pre> -<ol> -<li>メールを書く。</li> -<li>送信側のMTAに送る。</li> -<li>受信側のMTAに転送する。</li> -<li>受信者が受信側のMDAに新着メールの有無を確認する。</li> -</ol> +<h2>CRC(巡回冗長検査)</h2> <p> -SMTPサーバーどうしが通信する際、スパムメールをフィルタリングする為、いくつかの方法で送信元の本人確認を行う。その際第三者としてDNS及び、サーバーのIPアドレスを管理している人(今回の場合はさくらインターネット)が登場する。DNSにはドメインをIPアドレスにひもづける情報が登録できる。またIPアドレスの管理人には、自分が借りているIPアドレスを自分のドメインにひもづける情報を登録してもらう。これらによって、メールの送信者がドメイン、IPアドレス双方から確認できるので、なりすましを防止できる仕組みである。</p> - +入力のデータをごにょごにょしてある値を出力する。</p> +<blockquote cite="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB"> <p> -最近はgmail等の大手がスパム対策を強化する一環として、以下に述べるような本人確認をすべて通らなければ受信しないようにしているらしい。</p> - -<h2>MTAどうしのやりとり</h2> +データ転送等に伴う偶発的な誤りの検査によく使われている<sup>[5]</sup>。 +</p> +</blockquote> <p> -以下、MTAどうしの通信内容をざっくり説明する。 +らしい。 </p> - -<h3>Aレコード、MXレコードによる送信先の特定</h3> <p> -MTAがメールを送信する際、まずは宛先のサーバーを特定する必要がある。この際に使用するのがDNSに登録されているMXレコードである。送信元のMTAはまずDNSに宛先のドメインのMXレコードを問い合わせる。するとメールの送信先のドメイン名が分かるので、続いてこの送信先のIPアドレスを問い合わせる:</p> -<pre> -MTA (mtkn.jp <i>sss.sss.sss.sss</i>) DNS - - example.com宛のメールって - 誰に送ったらええん? - ----------------------------------------&gt; - - mai.example.comやで(MXレコード) - &lt;---------------------------------------- - - mail.example.comってどこや? - ----------------------------------------&gt; - - <i>ddd.ddd.ddd.ddd</i>やで(Aレコード) - &lt;---------------------------------------- -</pre> +入力のビットを一列に並べて、除数で「割り算」していく。この「割り算」が多項式の除算に似ているので、この除数をCRC多項式というらしい。ただし多項式の除算と違い、引き算するところをXORする。CRC32の場合、除数は33ビットである。33ビットで割ると32ビットの余りが残る。この余りがCRC32のチェックサムである。除数は色々あるようだが、標準的なものがWikipedia<sup>[5]</sup>に列挙されている。除数<code>1011</code>を使ったCRC3の計算の手順は以下の通り: +</p> +<pre><code>1110101011011100110101101101111 入力(適当) +1011 除数(4ビット) +------------------------------- + 101101011011100110101101101111 結果(入力と除数のXOR) + 1011 + ------------------------------ + 00001011011100110101101101111 + 1011 + ------------------------- + 000011100110101101101111 + 1011 + -------------------- + 1010110101101101111 + 1011 + ------------------- + 001110101101101111 + 1011 + ---------------- + 101101101101111 + 1011 + --------------- + 00001101101111 + 1011 + ---------- + 110101111 + 1011 + --------- + 11001111 + 1011 + -------- + 1111111 + 1011 + ------- + 100111 + 1011 + ------ + 01011 + 1011 + ---- + 000 CRC3チェックサム +</code></pre> <p> -次にこの宛先のMTAとの通信を開始する。しかし受信側のMTA(example.com)は、mtkn.jpを名乗るサーバーが本当にmtkn.jpかどうか確認する必要がある:</p> -<pre> -MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>) - こんちは。mtkn.jpです。 - ----------------------------------------&gt; +普通の割り算と基本は同じであるが、引き算の部分だけXORになっている。</p> +<p> +以上の計算をプログラムの先頭252バイトに対して、33ビットの除数を用いて行う。データの並べ方は、上の例において左側を先頭に、フラッシュROM上の0番地から、各バイトは最上位ビットから順に並べる。入力のデータは253バイト目から256バイト目に<code>0</code>をひっつけて計算する。これは多分予め長さが分からないデータでも計算できるようにしたかったからかな。除数は<code>0x104c11db7</code>である(最上位ビットは常に1なのでデータシートでは省略されている)。</p> +<p> +入力データは1バイトづつ処理したいみたいである。多分通信等で使う都合である。この時XORは結合則が成り立つので1バイト処理した結果と次のバイトとをXORして次の処理の入力として利用することができる: +</p> +<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当) +|......| +111000110000000000000000000000000 先頭1バイト +100000100110000010001110110110111 除数 +------------------------------------------------------------------------ +011000010110000010001110110110111 + 100000100110000010001110110110111 + ----------------------------------------------------------------------- + 010000001010000110010011011011001 + 100000100110000010001110110110111 + ---------------------------------------------------------------------- + 000000110010001110101000000000101 +|......| + 110010001110101000000000101000000 1バイト目の結果 + |......| + 10000001 入力の2バイト目 + ---------------------------------------------------------------- + 010010011110101000000000101000000 1バイト目の結果と2バイト目のXOR + 100000100110000010001110110110111 除数 + ---------------------------------------------------------------- + 000100011011010010001111100110111 + . + . + . +</code></pre> +<p> +以上の操作は以下のようなアルゴリズムのループで実装できる。</p> +<ul> +<li>前回の結果と、入力データの次のバイトをXOR</li> +<li> + <ul> + <li>先頭の1ビットが1の場合、除数とXORを取り左シフト</li> + <li>先頭の1ビットが0の場合、そのまま左シフト</li> + </ul> +</li> +</ul> +<p> +これを<code>for</code>ループで回す都合上、最初のバイトもXORを取る。上の例では最初は<code>0x0</code>とXORを取っているが、この値を<code>0x0</code>以外にすることもできる。そうした方がいろいろいいこともあるらしい。RP2040では<code>0xffffffff</code>を使う。更にこの工程を32ビットの<code>int</code>だけで行うことを考える: +</p> +<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当) - なりすましちゃうか確認したろ -</pre> +11111111111111111111111111111111 0xffffffff +11100011000000000000000000000000 先頭1バイトを24ビットシフト +-------------------------------- XOR +00011100111111111111111111111111 +先頭1ビットが0なので1ビットシフト +-------------------------------- シフト +00111001111111111111111111111110 +先頭1ビットが0なので1ビットシフト +-------------------------------- シフト +01110011111111111111111111111100 +先頭1ビットが0なので1ビットシフト +-------------------------------- シフト +11100111111111111111111111111000 +先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR: +11001111111111111111111111110000 シフト +00000100110000010001110110110111 除数の下位32ビット +-------------------------------- XOR +11001011001111101110001001000111 +先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR: +10010110011111011100010010001110 シフト +00000100110000010001110110110111 除数の下位32ビット +-------------------------------- XOR +10010010101111001101100100111001 +先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR: +00100101011110011011001001110010 シフト +00000100110000010001110110110111 除数の下位32ビット +-------------------------------- XOR +00100001101110001010111111000101 +先頭1ビットが0なので1ビットシフト +-------------------------------- シフト +01000011011100010101111110001010 +先頭1ビットが0なので1ビットシフト +-------------------------------- シフト +10000110111000101011111100010100 1バイト目の結果 -<h3>SPFレコードによる送信元の本人確認</h3> +10000001 入力の2バイト目 +-------------------------------- XOR +00000111111000101011111100010100 +先頭1ビットが0なので1ビットシフト +-------------------------------- シフト +00001111110001010111111000101000 +. +. +. +</code></pre> <p> -受信側のMTAは、自分に連絡してきたサーバーが本人かどうかをDNSに問い合わせる。このときに使うのがSPFレコードである。このレコードにはドメインの所有者が、自分のドメインからメールを送信してもいいIPアドレスを指定する。受信側のMTAは送信元のIPアドレスと、このレコードに記載されているIPアドレスを比べることで、送信側のMTAがドメインの所有者に認められているMTAかどうか判断できる。</p> -<pre> -DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) - mtkn.jpを名乗るやつから連絡 - 来てるんやけどこいつ本物? - &lt;---------------------------------------- - - IPアドレスが<i>sss.sss.sss.sss</i> - やったら多分本物やわ。(SPFレコード) - ----------------------------------------&gt; +これを実装したのが以下のコード:</p> +<pre><code>uint32_t +crc32(uint8_t *idata, size_t len) +{ + uint32_t pol = 0x04C11DB7; + uint32_t c = 0xFFFFFFFF; + uint32_t b; - よっしゃ -</pre> + for (int i = 0; i &lt; len; i++) { + b = idata[i] &lt;&lt; 24; + c ^= b; + for (int j = 0; j &lt; 8; j++) { + c = c &gt;&gt; 31 & 1 ? c &lt;&lt; 1 ^ pol : c &lt;&lt; 1; + } + } -<h3>PTRレコード</h3> + return c; +} +</code></pre> <p> -次は逆にIPアドレスからドメインを辿って本人確認する。これに使われるのがPTRレコードである。このレコードはIPアドレスの管理人(ここではさくらインターネット)でないと登録できない。しかし、IPアドレスを借りるにあたって、自分の名前やら住所やらを提供しており、PTRレコードをきちんと設定した上でスパムメールを送ると、自分の身元がバレるので、スパムの抑止に繋がる。</p> -<pre> -DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) - <i>sss.sss.sss.sss</i>ってIPアドレスで - mtkn.jpってドメイン登録されてる? - &lt;---------------------------------------- - - されてるで。(PTRレコード) - ----------------------------------------&gt; +<code>main()</code>関数では上の<code>crc32()</code>に、<code>idata</code>として入力となるバイナリデータの先頭を、<code>len</code>として<code>252</code>を渡してCRC32を計算させる。その後、出力先のファイルに入力元のデータをコピーしていき、253バイト目から256バイト目だけ、計算したCRC32に置き換える。入力元のこの場所にデータが書き込まれていないかどうかは確かめていない。 +</p> - よっしゃ -</pre> +<h2>UF2(USB Flashing Format)</h2> +<p> +Microsoftが開発したフラッシュ書き込み用のファイル形式らしい: +<blockquote cite="https://github.com/microsoft/uf2"> +<p> +UF2 is a file format, developed by Microsoft for PXT (also known as +Microsoft MakeCode), that is particularly suitable for flashing microcontrollers +over MSC (Mass Storage Class; aka removable flash drive)<sup>[6]</sup>. +</p> +</blockquote> +<p> +このファイルに変換する上で必要な情報はGitHubのmicrosoft/uf2<sup>[6]</sup>に表として纏められている: +<blockquote cite="https://github.com/microsoft/uf2"> +<table> +<thead><tr> +<th>Offset</th><th>Size</th><th>Value</th> +</tr></thead> +<tbody> +<tr> +<td>0</td> +<td>4</td> +<td>First magic number, <code>0x0A324655</code> (<code>"UF2\n"</code>)</td> +</tr> +<tr> +<td>4</td> +<td>4</td> +<td>Second magic number, <code>0x9E5D5157</code></td> +</tr> +<tr> +<td>8</td> +<td>4</td> +<td>Flags</td> +</tr> +<tr> +<td>12</td> +<td>4</td> +<td>Address in flash where the data should be written</td> +</tr> +<tr> +<td>16</td> +<td>4</td> +<td>Number of bytes used in data (often 256)</td> +</tr> +<tr> +<td>20</td> +<td>4</td> +<td>Sequential block number; starts at 0</td> +</tr> +<tr> +<td>24</td> +<td>4</td> +<td>Total number of blocks in file</td> +</tr> +<tr> +<td>28</td> +<td>4</td> +<td>File size or board family ID or zero</td> +</tr> +<tr> +<td>32</td> +<td>476</td> +<td>Data, padded with zeros</td> +</tr> +<tr> +<td>508</td> +<td>4</td> +<td>Final magic number, <code>0x0AB16F30</code></td> +</tr> +</tbody> +</table> +</blockquote> -<h3>DKIM</h3> <p> -送信側MTAが本人であることを証明するため、受信側に送るメールに署名する。署名は公開鍵方式で行うが、この鍵をドメインキーというようである。この署名が付いたメールをDKIM(DomainKey Identified Mail)という。送信側はあらかじめDNSにドメインキーの公開鍵を登録しておき、メール送信時に秘密鍵で署名する:</p> -<pre> -MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>) +RP2040のデータシート<sup>[3]</sup>「2.8.4.2 UF2 Format Details」を見ると、8バイト目のFlagsは、28バイト目にファミリーIDが書き込まれていることを示す<code>0x00002000</code>、12バイト目は、書き込みを行うフラッシュROMの先頭アドレスである<code>0x10000000</code>に、各ブロックの先頭からの位置を足したもの、16バイト目の、各ブロックのデータサイズは256バイト、28バイト目のファミリーIDは<code>0xe48bff56</code>である。あとは表の通り3つのマジックナンバーをセットし、32バイト目以降にデータを書き込み、20バイト目と24バイト目にブロックの通し番号と総数をそれぞれ書き込めばいい。ブロックの通し番号はデータのついでに書き込めるが、総数はデータを全部さばいた後でないと分からないので、最後全てのブロックにまとめて書き込むようにした。できたのが以下のコード: +</p> +<pre><code>#include &lt;stdio.h&gt; +#include &lt;stdint.h&gt; +#include &lt;stdlib.h&gt; +#include &lt;string.h&gt; - 本人確認できたわ - おまたせ。 - &lt;---------------------------------------- - userさんにメール送るで。 - 署名も付けるで。 - ----------------------------------------&gt; -</pre> -<p> -受信側はDNSから送信側のドメイン鍵の公開鍵をダウンロードし、署名を確認する:</p> -<pre> -DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) - mtkn.jpが署名付きでメール - くれたんやけどこの署名本物? - &lt;---------------------------------------- +size_t +fwrite32l(uint32_t d, FILE *f) +{ + int i; + uint8_t b; + for (i = 0; i &lt; 32; i += 8) { + b = (uint8_t) (d &gt;&gt; i & 0xff); + fwrite(&amp;b, 1, 1, f); + if (ferror(f)) { + fprintf(stderr, "Fwrite32l: write error.\n"); + return 0; + } + } + return 4; +} - これで確認してみて(DKIM公開鍵) - ----------------------------------------&gt; +int +main(int argc, char *argv[]) +{ + FILE *src = NULL, *dst = NULL; + size_t sdata = 476; + int retnum = 0; - よっしゃ -</pre> + uint32_t mag1 = 0x0A324655; + uint32_t mag2 = 0x9E5D5157; + uint32_t flags = 0x00002000; // familyID present + uint32_t addr = 0x10000000; + uint32_t nbyte = 256; + uint32_t blk = 0; + uint32_t nblk = 0; + uint32_t famid = 0xe48bff56; + uint8_t data[sdata]; + uint32_t mag3 = 0x0AB16F30; -<h3>DMARCレコード(上記の本人確認で詐欺だと発覚した場合)</h3> -<p> -この他DNSにはDMARCレコードというものを登録することができる。これは、以上の本人確認により送信側がにせものであることが確認できた際、受信側にどのような行動を取ってほしいかを記述するものである。これにより例えば、自分を名乗る詐欺師がどこかにメールを送信した場合、受信したサーバーから自分宛てに通報するように頼める(この頼みが聞き入れられるかどうかは多分受信側MTAによる)。 -</p> -<pre> -DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>) - mtkn.jpを名乗る詐欺師から - メールきたんやけどどないしょ? - &lt;---------------------------------------- + memset(data, 0, sdata); - ここ(postmaster@mtkn.jp)に通報や。 - (DMARCレコード) - ----------------------------------------&gt; + if (argc != 3) { + fprintf(stderr, "Usage: %s src dst\n", argv[0]); + exit(1); + } - よっしゃ -</pre> + if ((src = fopen(argv[1], "rb")) == NULL) { + fprintf(stderr, "Could not open %s.\n", argv[1]); + retnum = 1; + goto defer; + } + if ((dst = fopen(argv[2], "wb")) == NULL) { + fprintf(stderr, "Could not open %s.\n", argv[2]); + retnum = 1; + goto defer; + } + + while (!feof(src)) { + fwrite32l(mag1, dst); + fwrite32l(mag2, dst); + fwrite32l(flags, dst); + fwrite32l(addr, dst); + fwrite32l(nbyte, dst); + fwrite32l(blk, dst); + fwrite32l(nblk, dst); // dummy + fwrite32l(famid, dst); -<h2>サーバーの設定</h2> -<h3>VPSの契約、ドメインの取得</h3> -<p> -VPSとドメインを契約する。今回はVPSをさくらインターネットで、ドメインをムームードメインでそれぞれ契約した。さくらのVPSは好きなISOからOSをインストールでき、管理画面も分かりやすい(他のVPSを知らないので比較はできないが)。ムームードメインは安い。管理画面も悪くはない。以前お名前.comでも借りたことがあるが、こちらは広告のメールがやたら届いてうっとうしかった記憶がある。</p> + fread(data, 1, nbyte, src); + if (ferror(src)) { + fprintf(stderr, "Read error: %s.\n", argv[1]); + retnum = 1; + goto defer; + } + fwrite(data, 1, sdata, dst); + if (ferror(src)) { + fprintf(stderr, "Write error: %s.\n", argv[2]); + retnum = 1; + goto defer; + } -<h3>OpenBSDのインストール</h3> -<p> -さくらのVPSにOpenBSDをインストールする。さくらインターネットが用意したISOがあるので、OS再インストールの画面からそれを選べばすぐにインストールきる。でもなんとなく気持悪いのでopenbsd.orgから本家をダウンロードした。</p> -<p> -インストールのこまかい手順は割愛するが、ひとつだけひっかかった点があるので書いておく。インストール先のディスクをセットアップし、OSに必要なファイルをインストールメディアからディスクにコピーすると以下のエラーがでてカーネルパニックになった: -</p> -<pre><code>wdc_atapi_start: not ready, st = 50 -fatal protection fault in supervisor mode -trap type 4 code 0 rip ffffffff810081a9 cs 8 rflags 10282 cr 2 23a896000 cpl 6 rsp ffff80000e9df410 -gsbase 0xffffffff81908ff0 kgsbase 0x0 -panic: trap type 4, code=0, pc=ffffffff810081a9 -syncing disks...18 18 18 18 18 18 18 18 + fwrite32l(mag3, dst); + + addr += nbyte; + blk++; + nblk++; + } + + for (int i = 0; i &lt; nblk; i++) { + if (i == 0) + if (fseek(dst, 24, SEEK_SET) &lt; 0) { + fprintf(stderr, "Seek error: %s.\n argv[2]"); + retnum = 1; + goto defer; + } + fwrite32l(nblk, dst); + if (i &lt; nblk - 1) + if(fseek(dst, 512 - 4, SEEK_CUR) &lt; 0){ + fprintf(stderr, "Seek error: %s.\n argv[2]"); + retnum = 1; + goto defer; + } + } + +defer: + if (src) + fclose(src); + if (dst) + fclose(dst); + return retnum; +} </code></pre> +<p><code>fwrite32l()</code>関数は指定されたファイルに32ビットの整数を下位バイトから順に書き込む関数である。バイトオーダーとかややこしそうなので作っておいたけど必要なのかな?あと名前が気に入らない。</p> <p> -原因はあまり調べていないが、どうもインストールメディアの読み込みに失敗しているようで、インストールに必要なファイルをウェブ上からダウンロードする方法を選択すると問題なくインストールできた。 +CRC32のチェックサムが書き込まれたバイナリファイルを、このプログラムでUF2に変換し、生成されたファイルをUSBストレージとして接続したRP2040にコピーすればフラッシュROMに書き込まれる。 </p> -<h3>ファイアーフォールの設定</h3> +<h2>Flash Second Stage</h2> <p> -メールの送受信等に必要なポートを開ける:</p> -<ul> -<li>TCP 22: ssh接続用</li> -<li>UDP 53: DNSとの通信用</li> -<li>TCP 25: SMTPの通信用</li> -<li>TCP 587: メールクライアントからMTAへの通信用(メール送信時)</li> -<li>TCP 993: メールクライアントからMDAへの通信用(メール受信時)</li> -<li>TCP 80, 443: <code>acme-client(1)</code>によるサーバー証明書の取得用</li> -</ul> +RP2040に電源を投入し、CRC32のチェックが通った後、フラッシュROMからコピーされたプログラムの先頭から実行が開始される。このコピーされた部分で、その後の動作に必要な各種の設定を行うことになる。RP2040のデータシートには、フラッシュROMとSSIコントローラのXIPを設定するようにと書かれている。XIPはExecute in Placeの略で、フラッシュROMの内容をCPUから直接実行するものである。SSIはSynchronous Serial Interfaceの略で、周辺機器と情報のやりとりをする通信方式である。RP2040はチップに内蔵されたこのSSIコントローラを通して、外部のフラッシュROMと通信しているのだが、このコントローラを適切に設定すればフラッシュROMの内容がCPUから直接アクセスできる<code>0x10000000</code>番地以降にマップされる。これによりフラッシュROMから内部のSRAMにデータをコピーすることなく命令を実行できるので、速くて便利だという。 +</p> <p> -以上の設定を<code>/etc/pf.conf</code>に設定する: +しかしこのSSIコントローラはSynopsysという会社のDW_apb_ssiというIPを使っているようで、データシートのSSIコントローラの章は多分Synopsysの人が書いている。その他の章はRaspberry Pi財団の書いたブリティッシュイングリッシュだが、この部分だけ多分ネイティブじゃない人の書いたいい加減な英語である。誤植も多い。何日かかけて理解しようとしたがよく分からん。不毛なので一旦諦めた。</p> +<p> +RP2040には内部にもROMがあり、はバージョン情報や電源を投入した時の動作、その他便利な関数が書き込まれている。この関数の中に外部のフラッシュROMとSSIコントローラを設定するものも含まれているので、今回はこれを利用した。ただしこの方法だとフラッシュROMとの通信方式がStandard SPIのままなので少し遅いらしい。詳しくはデータシートの「2.3.8. Bootrom Contents」を参照。 </p> -<pre><code>set skip on lo - -block return # block stateless traffic -pass in log proto tcp from any to any port 22 #ssh - -pass out proto udp from any to <i>DNSのIPアドレス</i> port 53 #DNS -pass out proto udp from any to any port 123 #ntp - -pass proto icmp #ping -pass proto tcp from any to any port { 80, 443 } #www -pass proto tcp from any to any port { 25, 587, 993 } #mail -</code></pre> <p> -<code>pf(4)</code>の設定を反映: +RP2040の内蔵ROMの<code>0x00000018</code>番地に関数を検索するための関数がある。この関数に<code>0x00000014</code>番地の<code>rom_func_table</code>と、各関数に割り当てられた二文字の文字列を渡せば、欲しい関数へのポインタが返ってくる。なお、二文字の文字列はそれぞれASCIIコードで現し、二文字目を8ビットシフトしたものと1文字目のORを取ったものを渡すことになっている。今回欲しい関数はフラッシュROMをXIPに設定するもの(<code>_flash_enter_cmd_xip()</code>)なので、<code>'C', 'X'</code>を渡す。関数のポインタが返ってきて、それを呼び出せばフラッシュROMとSSIはXIPモードになる: </p> -<pre><code># pfctl -f /etc/pf.conf +<pre><code>setup_xip: + ldr r3, rom_base + + ldrh r0, [r3, #0x14] // rom_func_table + ldr r1, =('C' | 'X' &lt;&lt; 8) // _flash_enter_cmd_xip() + ldrh r2, [r3, #0x18] // rom_table_lookup + blx r2 + blx r0 +/* ... */ +rom_base: + .word 0x00000000 </code></pre> -<h3>DNSの設定</h3> -<h4>A, MX, SPF, DMARC</h4> <p> -DNSのレコードを設定する。ムームーDNSの場合、コントロールパネルにログインし、サイドバーからドメイン管理&gt;ドメイン操作&gt;ムームーDNSと進み、設定するドメインの変更ボタンをクリックすると設定画面がでるので、以下のように設定する:</p> +XIPの設定が完了すれば、次はメインのプログラムを実行するための準備である。エントリーポイントの指定、スタックポインタの初期値の設定、ベクターテーブルの設定である。Armのマニュアル<sup>[7]</sup>によると、初期スタックポインタとエントリーポイントはベクターテーブルの<code>0x0</code>バイト目と<code>0x4</code>バイト目に書くことになっている:</p> +<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/Exception-number-definition"> <table> -<thead> -<tr> -<th>サブドメイン</th> -<th>種別</th> -<th>内容</th> -<th>優先度</th> -</tr> -</thead> -<tbody> -<tr> -<td></td> -<td>A</td> -<td><i>sss.sss.sss.sss</i></td> -<td></td> -</tr> -<tr> -<td>mail</td> -<td>A</td> -<td><i>sss.sss.sss.sss</i></td> -<td></td> -</tr> -<tr> -<td></td> -<td>MX</td> -<td>mail.mtkn.jp</td> -<td>10</td> -</tr> -<tr> -<td></td> -<td>TXT</td> -<td>v=spf1 mx -all</td> -<td></td> -</tr> -<tr> -<td>_dmarc</td> -<td>TXT</td> -<td>v=DMARC1;p=reject;rua=mailto:postmaster@mtkn.jp</td> -<td></td> -</tr> -</tbody> +<caption> +Table 7.3. Exception numbers +</caption><colgroup><col><col></colgroup><thead><tr><th>Exception number</th><th>Exception</th></tr></thead><tbody><tr><td>1</td><td>Reset</td></tr><tr><td>2</td><td>NMI</td></tr><tr><td>3</td><td>HardFault</td></tr><tr><td>4-10</td><td>Reserved</td></tr><tr><td>11</td><td>SVCall</td></tr><tr><td>12-13</td><td>Reserved</td></tr><tr><td>14</td><td>PendSV</td></tr><tr><td>15</td><td>SysTick, optional</td></tr><tr><td>16</td><td>External Interrupt(0)</td></tr><tr><td>...</td><td>...</td></tr><tr><td>16 + N</td><td>External Interrupt(N)</td></tr></tbody> </table> +</blockquote> +<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/The-vector-table"> +<table> +<caption> +Table 7.4. Vector table format +</caption><colgroup><col><col></colgroup><thead><tr><th>Word offset in table</th><th>Description, for all pointer address values</th></tr></thead><tbody><tr><td>0</td><td>SP_main. This is the reset value of the Main stack pointer.</td></tr><tr><td>Exception Number</td><td>Exception using that Exception Number</td></tr></tbody> +</table> +</blockquote> <p> -ひとつめのAレコードは、ドメイン名(mtkn.jp)にIPアドレス(<i>sss.sss.sss.sss</i>)を紐付けるものである。ふたつめはmail.mtkn.jpのIPアドレスを登録するものである。みっつめのMXレコードは、このドメイン宛てのメール(user@mtkn.jp等)をどこのサーバーに送信するかを決めるものである。上記の設定では、mail.mtkn.jpに送信するようにしている。以上の設定で、user@mtkn.jp宛てのメールは、mail.mtkn.jpというサーバーに送信すればよいことが分かり、さらにこのサーバーのIPアドレスは<i>sss.sss.sss.sss</i>であることも分かる。 -</p> -<p> -次のTXTレコードはSPFレコードである。v=spf1でSPFのバージョンを示し、mxでドメインのMXレコードに登録されたIPアドレスからの送信を許可し、-allでそれ以外のIPアドレスからの送信を禁止している。 -</p> -<p> -最後のものはDMARCレコードである。v=DMARC1でバージョンを示し、p=rejectで、本人確認をクリアしなかったメールについて、その受信者が受信を拒否するように指定している。rua=mailto:postmaster@mtkn.jpでは、本人確認をクリアしなかったメールを受信した場合の通報先を指定している。</p> +RP2040のベクターテーブルはM0PLUS: VTOR(<code>0xe0000000 + 0xed08</code>)というレジスタに書き込むことで設定する。このとき、下位8ビットは0にしないといけないので、ベクターテーブルの位置は256バイトでアラインする必要がある。ベクターテーブルの定義は<code>main.s</code>に書き、<code>boot2.s</code>からはラベルを使ってアクセスすることにする。以上をまとめると以下のコードになる:</p> +<pre><code> ldr r0, =vectors + ldr r1, m0plus_vtor + str r0, [r1, #0] // vector table + ldr r1, [r0, #4] // entry point + ldr r0, [r0, #0] // stack pointer + mov sp, r0 + bx r1 -<h4>PTR</h4> -<p>PTRレコードを設定する。さくらのVPSの場合、VPS管理画面にログインし、ネットワーク>ホスト名逆引き登録と進み、カスタムホスト名に自分のドメイン名(mtkn.jp)を入力する。 -</p> +/* ... */ -<h3>OpenSMTPDの設定</h3> -<h4>ユーザーの作成</h4> -<p> -メールの送受信を担当するユーザーを作成する:</p> -<pre><code># useradd -m -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail +m0plus_vtor: + .word 0xe0000000 + 0xed08 </code></pre> +<p>なお以上のコードは<code>.boot2</code>という名前のセクションにしてある。 +</p> -<h4>サーバー証明書の取得</h4> -<p> -<code>httpd(8)</code>の設定ファイルを作成する。<code>/etc/examples</code>からサンプルの設定ファイルをコピーして編集する。変更箇所はドメイン名だけでいい。編集後、httpdを起動する。</p> -<pre><code># cp /etc/examples/httpd.conf /etc/ -# vi /etc/httpd.conf -# echo httpd_flags= &gt;&gt; /etc/rc.conf.local -# rcctl start httpd -# rcctl enable httpd -</code></pre> +<h2>メインのコード(<code>main.s</code>)</h2> +<h3>ベクターテーブル</h3> <p> -続いて<code>acme-client(1)</code>を使ってLet's Encryptでサーバー証明書を発行する。まずはこちらもサンプルの設定ファイルをコピーしてドメイン名を変更する。その後、<code>acme-client(1)</code>を実行し、さらに<code>cron(8)</code>に登録することで、証明書が自動で更新されるようにする。</p> -<pre><code># cp /etc/examples/acme-client.conf /etc/ -# vi /etc/acme-client.conf -# acme-client -v mail.mtkn.jp -# crontab -e -</code></pre> - -<pre><code>#minute hour mday month wday [flags] command -~ 2 * * * acme-client mail.mtkn.jp &amp;&amp; rcctl restart smtpd +上で説明したように、ベクターテーブルのアドレスは256バイトの境界にないといけないが、<code>boot2.s</code>をフラッシュの最初の256バイトに配置しており、<code>main.s</code>はその直後から始まるようにリンクする。そのためメインのコードの最初にベクターテーブルを配置すればいい。ここでは割り込みの処理は考えないので、初期スタックポインタとエントリーポイントだけである。初期スタックポインタはSRAMの最後?(<code>0x20040000</code>)、エントリーポイントはエントリーポイントのラベルを用いて設定した。また、別のファイル(<code>boot2.s</code>)からアクセスしたいので、<code>.global</code>宣言をつけておく: +</p> +<pre><code> .global vectors +vectors: + .word 0x20040000 // initial SP + .word (reset+1) </code></pre> +<p> +<code>reset</code>ラベルに<code>1</code>を足しているのはRP2040がThumbモードのみに対応しているからである。ArmのCPUはArmモードとThumbモードがあり、Armモードは32ビットの命令で高機能。Thumbモードは16ビットの命令(一部32ビット)でコンパクトである。どちらのモードでも命令は2の倍数のアドレスに並ぶことになる。そのためジャンブ命令のジャンプ先のアドレスの最下位ビットは常に0である。この最下位ビットはジャンプ先のモードを示す為に利用される。両方のモードに対応したCPUではジャンプ先のアドレスの最下位ビットが0ならArmモード、1ならThumbモードに切り替わる。ブランチ命令のオペランド等は多分アセンブラがいい感じにしてくれるので単にラベルを書けば動く。ベクターテーブルのこの部分は自分で足す必要があるみたい。あんまりちゃんと調べてないのでマニュアル読んでや。</p> -<h4>ドメインキーの作成とDNSへの登録</h4> +<h3>GPIOの設定</h3> <p> -OpenSMTPD用のDKIMフィルターをインストールする。 +電源投入直後、RP2040の周辺機器はリセット状態になっている。まずは今回利用するGPIOのリセット状態を解除する必要がある。データシートの「2.14. Subsystem Resets」には以下のように書かれている: </p> -<pre><code># pkg_add opensmtpd-filter-dkimsign -</code></pre> - +<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf"> <p> -ドメインキーとしてRSAの秘密鍵と公開鍵を作成する。DNSのTXTレコードの制約により、鍵の長さは1024ビットにする。 +Every peripheral reset by the reset controller is held in reset at power-up. +It is up to software to deassert the reset of peripherals it intends to use. </p> -<pre><code># openssl genrsa -out /etc/mail/dkim/private.key 1024 -# openssl rsa -in /etc/mail/dkim/private.key -pubout -out /etc/mail/dkim/public.key -# chown -r _dkimsign:_dkimsign /etc/mail/dkim -# chmod 0400 /etc/mail/dkim/private.key -</code></pre> - -<p>作成したドメインキーの公開鍵をDNSに登録する:</p> -<table> -<thead> -<tr> -<th>サブドメイン</th> -<th>種別</th> -<th>内容</th> -<th>優先度</th> -</tr> -</thead> -<tbody> -<tr> -<td>selector1._domainkey.mail</td> -<td>TXT</td> -<td>v=DKIM1;k=rsa;p=<i>公開鍵</i></td> -<td></td> -</tr> -</tbody> -</table> - +</blockquote> <p> -サブドメインのselector1は以下のsmtpd.confで<code>filter-dkimsign</code>の-sオプションに -指定してものと同じものであれば他のものでもいい。</p> - -<h4>smtpd.confの設定</h4> +リセット状態を解除するには、RESETS_BASE(<code>0x4000c000</code>)から<code>0x0</code>バイト目のRESETS: RESETレジスタのうち利用したい周辺機器のビットを<code>0x0</code>にすればいい。 +GPIOはIO Bank 0なので(これ明記されてなくない?)、RESETS: RESETレジスタのIO_BANK0(5番ビット)を<code>0x0</code>にする。 +</p> +<h4>レジスタのアトミックなクリア</h4> <p> -/etc/mail/smtpd.confを以下の通り変更:</p> -<pre><code># 認証に使用するサーバー証明書と秘密鍵を&quot;mtkn.jp&quot;という名前で登録。 -# 証明書のファイルは先程acme-client(1)で取得したもの。 -pki mtkn.jp cert &quot;/etc/ssl/mail.mtkn.jp.fullchain.pem&quot; -pki mtkn.jp key &quot;/etc/ssl/private/mail.mtkn.jp.key&quot; - -# テーブルの登録 -# ローカルに届いたメールの転送を設定するテーブル -table aliases file:/etc/mail/aliases -# ユーザーの名前と暗号化されたパスワードのテーブル -table passwd file:/etc/mail/passwd -# メールの転送を設定するテーブル -table virtuals file:/etc/mail/virtuals - -# フィルターの登録 -# ドメインキーによる署名を付加するフィルター -filter dkim_sign proc-exec\ &quot;filter-dkimsign -d mail.mtkn.jp -s selector1\ -k /etc/mail/dkim/private.key&quot; user _dkimsign group _dkimsign -# DNSの逆引き(PTRレコード)ができなかった場合に受信を拒否するフィルター -filter check_rdns phase connect match !rdns disconnect &quot;550 no rDNS.&quot; -# 正引き(Aレコード)と逆引き(PTRレコード)でDNSのレコードが一致しなかった -# 場合に受信を拒否するフィルター -filter check_fcrdns phase connect match !fcrdns disconnect &quot;550 no FCrDNS.&quot; - -# 各ネットワークインターフェース毎の処理を設定。 -listen on socket -listen on lo0 -# vio0というインターフェースの25番ポートに届いたものに関する設定。 -# 外部からmtkn.jp宛に届いたものの処理である。 -# 最初に登録した証明書と秘密鍵を使ってMTAの本人確認をし、 -# 上で設定したDNSに関するフィルターを適応。 -listen on vio0 port 25 tls pki mtkn.jp hostname &quot;mail.mtkn.jp&quot;\ filter { check_rdns, check_fcrdns } -# vio0というインターフェースのsubmission(587番)ポートに関する設定。 -# mtkn.jpのユーザー(e.g. user@mtkn.jp)から任意の人宛に送信される -# メールに関する設定。 -# 上と同様に&quot;mtkn.jp”という名前で登録されたpkiを使ってMTAの本人確認をし、 -# &quot;passwd&quot;という名前で登録したパスワードで認証し、 -# さらにドメインキーでメールに署名する。 -listen on vio0 mask-src port submission smtps pki mtkn.jp\ hostname &quot;mail.mtkn.jp&quot; auth &lt;passwd&gt; filter dkim_sign +RESETS: RESETレジスタのうち5番ビットだけを<code>0x0</code>にしたい。この時、まずこのレジスタを読み込んでから<code>~(1 &lt;&lt; 5)</code>と論理積を取って同レジスタに書き戻してもいいのだが、RP2040にはこれを一回の<code>str</code>でしかもアトミックにできる機能が用意されている。今回の場合アトミックかどうかは関係ないと思うけど。</p> +<p> +各レジスタには4個のアドレスが割り当てられている。データシートの各章のList of Registersに記載されているアドレスは通常の読み書きができる。そのアドレスに<code>0x1000</code>を足したものにアクセスするとアトミックなXORが、<code>0x2000</code>を足したものはアトミックなセットが、<code>0x3000</code>を足したものはアトミックなクリアができる。つまりレジスタのアドレスに<code>0x3000</code>を足したものに、<code>0x1 &lt;&lt; 5</code>を<code>str</code>すれば5番目のビットだけ<code>0x0</code>にして、他のビットは変更されない。逆に指定したビットだけ立てて他を触らない場合は<code>0x2000</code>を、あるいは指定したビットだけトグルしたい場合は<code>0x1000</code>を足したアドレスにアクセスすればいい。</p> +<h4>リセット状態の確認</h4> +<p>リセットの解除はすぐに完了するわけではないようである。リセットの解除が完了したかどうか確認するにはRESETS: RESET_DONEレジスタ(RESETS_BASEから<code>0x8</code>バイト目)の該当するビット(ここでは5番目のビット)を読む。この値が<code>0x1</code>であればリセットの解除が完了している。<code>0x0</code>であれば処理が進行中なので<code>0x1</code>が返ってくるまで繰り返し読み込んで<code>0x0</code>になるまで待機する。ところでこのレジスタはリセットの解除が完了したかどうか確かめるものなので、RESET_DONEという名前はどうなん? +<p> +以上から、GPIOのリセットを解除するのは以下のコード: +</p> +<pre><code>reset: + // unreset gpio + mov r0, #1 + lsl r0, r0, #5 // io_bank0 + ldr r3, resets_base + ldr r1, atomic_clr + str r0, [r3, r1] // RESETS: RESET +reset_chk: + ldr r1, [r3, #0x8] // RESETS: RESET_DONE + tst r0, r1 + beq reset_chk -# 各種メールの処理を定義する。 -# 各処理が実行される条件はこの後定義する。 -# ローカルメールはデフォルトのまま -action &quot;local_mail&quot; mbox alias &lt;aliases&gt; -# 自分のドメイン宛のメールは/var/vmail/以下に振り分けてmaildir形式で保存。 -action &quot;domain&quot; maildir &quot;/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}&quot;\ virtual &lt;virtuals&gt; -# 他のドメイン宛ては宛先へ転送。 -action &quot;outbound&quot; relay +/* ... */ -# どういう条件でどういう処理をするかを定義する。 -# 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。 -# ここでは/var/vmail/以下に振り分けて保存する。 -match from any for domain mtkn.jp action &quot;domain&quot; -# このサーバー内でのメールはlocal_mailとして処理。 -match from local for local action &quot;local_mail&quot; -# このサーバー内から他所へのメールは宛先に転送 -match from local for any action &quot;outbound&quot; -# 外部から来た他所宛てのメールは認証されているものに限り転送。 -# authを忘れると多分このサーバー経由で迷惑メールが送られる危険がある。 -match auth from any for any action &quot;outbound&quot; +atomic_clr: + .word 0x00003000 +resets_base: + .word 0x4000c000 </code></pre> -<h4>各テーブルの作成</h4> +<h3>GPIOの機能の選択</h3> +<p>RP2040のGPIOにはそれぞれ複数の機能が用意されていて、どれを使うかはソフトウェアから選択できる。利用できる機能の一覧と各機能の説明はデータシートの「2.19.2 Function Select」に詳しく書いてある。ここではGPIO25番のピンをSIO(Single-cycle IO)として利用する。同じCPUが載っているRaspberry Pi PicoはGPIO25番にLEDが半田付けされている。25番にしたのはこれに合わせるためである。他のピンでもいい。GPIOに1か0を印加するだけならこのSIOを使うみたいである。Single-cycleはCPUから操作したときに1クロックでその操作が完了するという意味らしい(本当か)。SIOの詳しい説明はデータシートの「2.3.1 SIO」にある。</p> <p> -上記の設定ファイルの<code>table</code>で指定したテーブルを作成する。<code>/etc/mail/aliases</code>は特に変更しなくてもいい。<code>/etc/mail/passwd</code>にはメールアカウントと暗号化されたパスワードを保存する。パスワードの暗号化には<code>smtpctl(8)</code>を使う:</p> -<pre><code># echo user@mtkn.jp:$(smtpctl encrypt) &gt;&gt; /etc/mail/passwd -<i>パスワードを入力し、エンター、Ctrl-D</i> -</code></pre> +GPIO25番の機能を選択するにはIO_BANK0_BASE(<code>0x40014000</code>)から<code>0xcc</code>番目のGPIO25_CTRLレジスタの下位5ビットに、該当する機能の番号を書き込めばいい。データシートの「2.19.2 Function Select」にある表を見ると、GPIO25番のSIOは5である:</p> +<pre><code> // set gpio functions + ldr r3, io_bank0_base + mov r0, #5 // sio + mov r1, #0xcc + str r0, [r3, r1] // IO_BANK0: GPIO25_CTRL -<p> -<code>/etc/mail/virtuals</code>にはメールの転送を設定できる。以下のようにする:</p> -<pre><code>abuse@mtkn.jp user@mtkn.jp -postmaster@mtkn.jp user@mtkn.jp -webmaster@mtkn.jp user@mtkn.jp -user@mtkn.jp vmail -</code></pre> -<p> -この設定で、例えばabuse@mtkn.jp宛てのメールがuser@mtkn.jpに転送される。abuse, postmaster, webmasterはなんか一応作っとけみたいに書いてたけど必要なんかな? -</p> +/* ... */ -<h3>Dovecotの設定</h3> -<p> -MDAとして<code>dovecot(1)</code>をインストールする。</p> -<pre><code># pkg_add dovecot +io_bank0_base: + .word 0x40014000 </code></pre> + +<h3>GPIOの出力を有効化</h3> <p> -dhparamを作成する(時間がかかる): +GPIO25番がSIOになったので、次にこのピンからの出力を有効化する。既定値では出力は無効になっている。ハイインピーダンスってことなのかな?出力を有効にするには、SIO_BASE(<code>0xd0000000</code>)から<code>0x24</code>バイト目のSIO: GPIO_OEレジスタの該当するビット(25番のピンなので25番ビット)を<code>0x1</code>にする: </p> -<pre><code># openssl dhparam -out /etc/ssl/dh.pem 4096 +<pre><code> // enable gpio output + ldr r3, sio_base + mov r0, #1 + lsl r0, r0, #25 // gpio25 + str r0, [r3, #0x24] // SIO: GPIO_OE + +/* ... */ + +sio_base: + .word 0xd0000000 </code></pre> +<h3>LEDの点滅</h3> +<p>以上でGPIOの設定は完了したので、あとは実際にLEDに電圧を掛けるだけである。レジスタのアドレスに<code>0x1000</code>を足したものに書き込むとアトミックなレジスタのXORができると書いたが、SIOはこの機能がサポートされていないようである。データシートの「2.1.2 Atomic Register Access」に、 +</p> +<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf"> <p> -わかりにくい設定ファイルをどけてシンプルなものに書き直す: +The SIO (Section 2.3.1), a single-cycle IO block attached directly to the cores' +IO ports, does <strong>not</strong> support atomic accesses at the bus level, +although some individual registers (e.g. GPIO) have set/clear/xor aliases. </p> -<pre><code># mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig -# vi /etc/dovecot/dovecot.conf -</code></pre> +</blockquote> <p> -ドキュメントは公式ウェブサイトで確認できる。分量が多すぎて読む気にならない。サンプルの設定ファイルにも各項目の説明と既定値が書いてあるがこれも分かりにくい。パスワードが漏洩するのはとりあえず避けたいので、<code>ssl = required</code>と暗号化されていないIMAPを<code>port = 0</code>として無効にする。他はとりあえず以下の設定で動く:</p> -<pre><code># IPv4は全て監視 -listen = * +と書かれている。そのかわりここにも書かれている通り、SIOの一部のレジスタにはアトミックなセット/クリア/XORをするためのレジスタが用意されている。ここではLEDを点滅させるためにGPIOの出力をトグルしたいのでXOR用のレジスタを使う。SIO_BASE(<code>0xd0000000</code>)から<code>0x1c</code>バイト目のSIO: GPIO_OUT_XORレジスタがそれである。このレジスタの25番ビットに<code>0x1</code>を書き込めばいい。出力をトグルした後は少し間をおいて同じことを繰り返す。間をおくためにここでは適当な数値を1づつ減らしていって0になったら返る関数<code>delay</code>を作った。タイマーと割り込みを使ったほうが消費電力等で優位なようだが、面倒なのでとりあえずこれで:</p> -# SSL接続のみ許可 -ssl = required +<pre><code> // blink led on gpio25 + ldr r4, sio_base + mov r5, r0 // r0 = 1 &lt;&lt; 25 +loop: + str r5, [r4, #0x1c] // SIO: GPIO_OUT_XOR + bl delay + b loop -# SSLに使用するファイル -ssl_cert = &lt;/etc/ssl/mail.mtkn.jp.fullchain.pem -ssl_key = &lt;/etc/ssl/private/mail.mtkn.jp.key -ssl_dh = &lt;/etc/ssl/dh.pem +delay: + mov r0, #1 + lsl r0, r0, #20 +delay_loop: + sub r0, r0, #1 + bne delay_loop + bx lr -# メールの保存場所は各ユーザーのホームディレクトリ。 -# ホームディレクトリの場所は以下のuserdbで設定。 -mail_location = maildir:~ +/* ... */ -# パスワードのファイル。 -# OpenSMTPDと共有。 -passdb { - args = scheme=BLF-CRYPT /etc/mail/passwd - driver = passwd-file -} +sio_base: + .word 0xd0000000 +</code></pre> +<p>なお以上のコードは<code>.text</code>セクションである。</p> -# ユーザーの設定。 -# ホームディレクトリは/var/vmail/<i>ドメイン</i>/<i>ユーザー名</i> -userdb { - args = uid=vmail gid=vmail home=/var/vmail/%d/%n - driver = static +<h2>リンカスクリプト</h2> +<p> +以上のコードには<code>.boot2</code>、<code>.text</code>の2つのセクションが含まれる。<code>.boot2</code>はフラッシュの先頭から256(<code>0x100</code>)バイト目まで、<code>.text</code>はその後ろに続くように配置する: +<pre><code>MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k } -# 使用するプロトコル。 -protocols = imap lmtp +SECTIONS +{ + .boot2 : { + *(.boot2) + . = 0x100; + } > FLASH -# SSLを使ったIMAPSのみを許可。 -service imap-login { - inet_listener imaps { - port = 993 - ssl = yes - } - # Disable imap - inet_listener imap { - port = 0 - } + .text : { + *(.text) + } > FLASH } </code></pre> -<pre><code># rcctl start dovecot -# rcctl enable dovecot -</code></pre> +<h2>Makefile</h2> <p> -サーバーの証明書が更新される際に<code>dovecot(1)</code>も再読み込みされるように<code>cron(8)</code>に追加しておく: +以上のソースコードは以下のように配置している: </p> -<pre><code>crontab -e +<pre>rp2040 +├── ex1 +│   ├── Makefile +│   ├── boot2.s +│   ├── main.s +│   └── memmap.ld +└── tools + ├── Makefile + ├── bin2uf2.c + └── bincrc.c +</pre> +<p> +toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。ex1ディレクトリのMakefileは以下の通り: +</p> +<pre><code>AS = arm-none-eabi-as +LD = arm-none-eabi-ld +OBJCOPY = arm-none-eabi-objcopy +BINCRC = ../tools/bincrc +BIN2UF2 = ../tools/bin2uf2 + +MCPU = -mcpu=cortex-m0plus +ASFLAGS = $(MCPU) +CFLAGS = $(MCPU) -ffreestanding -nostartfiles -O0 -fpic -mthumb -c +LDFLAGS = --no-relax -nostdlib + +all: tools led.uf2 + +clean: + rm -f *.o *.elf *.uf2 *.bin + cd ../tools &amp;&amp; make clean + +.s.o: + $(AS) $(ASFLAGS) -o $@ $&lt; + +led.elf: boot2.o main.o memmap.ld + $(LD) $(LDFLAGS) -o $@ -T memmap.ld boot2.o main.o + +led.bin: led.elf + $(OBJCOPY) -O binary led.elf $@ + +led.uf2: led.bin + $(BINCRC) led.bin led_crc.bin + $(BIN2UF2) led_crc.bin $@ + +flash: all + mount /dev/disk/by-label/RPI-RP2 /mnt + cp led.uf2 /mnt + +tools: + cd ../tools &amp;&amp; make </code></pre> -<pre><code>#minute hour mday month wday [flags] command -~ 2 * * * acme-client mail.mtkn.jp &amp;&amp; rcctl restart smtpd &amp;&amp; rcctl reload dovecot +<p> +RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し、ex1ディレクトリで</p> +<pre><code>$ make +# make flash </code></pre> +<p> +とすればプログラムがRP2040のボードに書き込まれて実行が開始される。</p> -<h3>テスト</h3> +<h2>最後に</h2> <p> -手元のメールクライアントから送受信のテストをする。特にgmail等に送った際に迷惑メールに分類されないかどうか確認する。以上の設定で、gmailへの送受信は問題なかった。また、<code>neomutt(1)</code>、<code>thunderbird(1)</code>からログイン及び送受信できることも確認した。</p> +光あれ。 +</p> -<h2>参考文献</h2> +<h2>参考</h2> <ul> -<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-1-what-is-email.html">Arch Linux mail server tutorial - part 1 - What is email?.unixsheikh.com</a></li> -<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-2-opensmtpd-dovecot-dkimproxy-and-lets-encrypt.html">Arch Linux mail server tutorial - part 2 - OpenSMTPD, Dovecot, DKIMproxy, and Let's Encrypt.unixsheikh.com</a></li> -<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-3-get-dns-right-it-is-important.html">Arch Linux mail server tutorial - part 3 - Get DNS right, it's important!.unixsheikh.com</a></li> -<li><a href="https://man.openbsd.org/smtpd">smtpd(8).OpenBSD Manual Pages</a></li> -<li><a href="https://man.openbsd.org/smtpd.conf">smtpd.conf(5).OpenBSD Manual Pages</a></li> -<li><a href="https://man.openbsd.org/httpd">httpd(8).OpenBSD Manual Pages</a></li> -<li><a href="https://man.openbsd.org/httpd.conf">httpd.conf(5).OpenBSD Manual Pages</a></li> -<li><a href="https://man.openbsd.org/acme-client">acme-client(1).OpenBSD Manual Pages</a></li> -<li><a href="https://man.openbsd.org/acme-client.conf">acme-client.conf(5).OpenBSD Manual Pages</a></li> -<li><a href="https://doc.dovecot.org/">Dovecot manual — Dovecot documentation</a></li> +<li> +[1] Hennesy, J. L. and Patterson, D. A. 2017. Computer Organization And Design RISC-V Edition. +</li> +<li> +[2] <a href="https://akizukidenshi.com/catalog/g/gK-17542/">RP2040マイコンボードキット.秋月電子通商</a> +</li> +<li> +[3] <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> +</li> +<li> +[4] <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> +</li> +<li> +[5] <a href="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB">巡回冗長検査.Wikipedia</a> +</li> +<li> +[6] <a href="https://github.com/microsoft/uf2">USB Flashing Format (UF2).GitHub</a> +</li> +<li> +[7] <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> +</li> </ul> ]]></description> </item> diff --git a/pub/sitemap.xml b/pub/sitemap.xml @@ -3,6 +3,7 @@ <url><loc>https://www.mtkn.jp/computer/9p.html</loc><lastmod>2025-12-19</lastmod></url> <url><loc>https://www.mtkn.jp/journal/posts/20241211.html</loc><lastmod>2025-12-11</lastmod></url> <url><loc>https://www.mtkn.jp/journal/posts/20241202.html</loc><lastmod>2025-12-02</lastmod></url> +<url><loc>https://www.mtkn.jp/computer/mailserver.html</loc><lastmod>2025-11-03</lastmod></url> <url><loc>https://www.mtkn.jp/</loc><lastmod>2025-08-27</lastmod></url> <url><loc>https://www.mtkn.jp/computer/rp2040_3_interrupt.html</loc><lastmod>2025-08-27</lastmod></url> <url><loc>https://www.mtkn.jp/computer/</loc><lastmod>2025-08-27</lastmod></url> @@ -29,7 +30,6 @@ <url><loc>https://www.mtkn.jp/journal/posts/20240428.html</loc><lastmod>2024-04-28</lastmod></url> <url><loc>https://www.mtkn.jp/computer/rp2040_2.html</loc><lastmod>2024-02-27</lastmod></url> <url><loc>https://www.mtkn.jp/computer/rp2040_1.html</loc><lastmod>2024-02-25</lastmod></url> -<url><loc>https://www.mtkn.jp/computer/mailserver.html</loc><lastmod>2024-02-24</lastmod></url> <url><loc>https://www.mtkn.jp/computer/git_server.html</loc><lastmod>2024-02-15</lastmod></url> <url><loc>https://www.mtkn.jp/kitchen/recipe/miso.html</loc><lastmod>2024-02-09</lastmod></url> <url><loc>https://www.mtkn.jp/about.html</loc><lastmod>2024-01-23</lastmod></url>