|
|
nono 1.4.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// Ethernet 基本クラス
//
#if !defined(SELFTEST)
#include "ethernet.h"
#include "config.h"
#include "hostnet.h"
#include "scheduler.h"
// コンストラクタ
EthernetDevice::EthernetDevice(uint objid_)
: inherited(objid_)
{
}
// デストラクタ
EthernetDevice::~EthernetDevice()
{
if ((bool)hostnet) {
hostnet->ResetRxCallback();
}
}
// 動的なコンストラクション
bool
EthernetDevice::Create()
{
// ホストドライバを作成してこのデバイスと紐付ける。
// ポート名は継承先ごとに Init() で設定している。
int n = GetId() - OBJ_ETHERNET0;
try {
hostnet.reset(new HostNetDevice(this, n, ""));
} catch (...) { }
if ((bool)hostnet == false) {
warnx("Failed to initialize HostNetDevice(%u) at %s", n, __method__);
return false;
}
auto func = ToDeviceCallback(&EthernetDevice::HostRxCallback);
hostnet->SetRxCallback(func, 0);
return true;
}
// これは Host スレッドから呼ばれる
void
EthernetDevice::HostRxCallback(uint32 dummy)
{
// スレッドを超えるためにメッセージを投げる
int n = GetId() - OBJ_ETHERNET0;
scheduler->SendMessage(MessageID::HOSTNET_RX(n));
}
// 設定ファイルから MAC アドレスを読み込む。
// MAC アドレスを保持するデバイスが呼び出すこと。MAC アドレスを保持する
// デバイスがイーサネットデバイスとは限らず、例えば LANCE を採用している
// 機種ではたいてい ROM か誰かが MAC アドレスを持っている。
//
// 引数 accept_rom が true なら、設定 ethernet-macaddr=rom を許可する。
// これは (お手持ちの実機) ROM イメージが MAC アドレスを保持しているため、
// 指定(や生成)不要なケースで指定できる (prom.cpp 参照)。
// 引数 accept_rom が false で、設定値に "rom" が指定されるとエラーにする。
//
// "rom" が指定されて許可されている場合 mac は触らず true を返す。
// MAC アドレスが指定されているか自動生成したかで取得できれば mac に格納して
// true を返す。それ以外の場合は、エラーメッセージを表示して false を返す。
/*static*/ bool
EthernetDevice::GetConfigMacAddr(uint n, MacAddr *mac, bool accept_rom)
{
std::string keyname = string_format("ethernet%u-macaddr", n);
const ConfigItem& item = gConfig->Find(keyname);
const std::string& val = item.AsString();
if (val == "rom") {
// ROM に書いてあるのを使う。
if (accept_rom) {
return true;
} else {
item.Err("\"rom\" cannot be specified in this vmtype");
return false;
}
} else {
if (val == "auto") {
// 自動生成。
// 出来れば生成したのを覚えといて、次からそれを使いたい。
if (0) {
} else {
mac->Generate();
}
} else {
// ユーザ指定
if (mac->FromString(val) == false) {
item.Err("Invalid MAC address");
return false;
}
}
}
return true;
}
#endif // !SELFTEST
// CRC32 を計算する。
//
// CRC と言ってもおそらく5つのパラメータによって定まる亜種が多数あるが、
// Ethernet で使われる CRC32 はおそらく以下のもの。
// 初期値: 0xffffffff
// 多項式: 0x04c11db6
// RefIn : false
// RefOut: false (出力をビットリバースしない)
// XorOut: false (出力を XOR しない)
//
// NetBSD の src/sys/net/if_ethersubr.c にある ether_crc32_be() がこれと同じ。
//
// その隣にある ether_crc32_le() は
// 初期値: 0xffffffff
// 多項式: 0xedb88320
// RefIn : true
// RefOut: false (出力をビットリバースしない)
// XorOut: false (出力を XOR しない)
// というパラメータだが、RefIn と多項式のビットが反転しているだけなので
// 結果をビットリバースすれば同じものになる。
//
// ちなみに ether_crc32_* の_le, _be はエンディアンではなく、どっち向きに
// 処理するかを示しており、名前が紛らわしい。
/*static*/ uint32
EthernetDevice::CRC32(const uint8 *buf, size_t buflen)
{
static const uint32 CRC_POLY = 0x04c11db6;
uint32 crc;
uint32 cy;
crc = 0xffffffff;
for (size_t i = 0; i < buflen; i++) {
uint32 s = buf[i];
for (size_t j = 0; j < 8; j++) {
cy = ((crc & 0x80000000U) ? 0x01 : 0x00) ^ (s & 0x01);
crc <<= 1;
s >>= 1;
if (cy) {
crc = (crc ^ CRC_POLY) | cy;
}
}
}
return crc;
}
// MAC アドレスの CRC32 を計算する限定版。
// 6バイトが uint64 にリトルエンディアン的に格納されていると分かっている。
/*static*/ uint32
EthernetDevice::CRC32(const MacAddr& mac)
{
static const uint32 CRC_POLY = 0x04c11db6;
uint32 crc;
uint64 s = mac.Get();
crc = 0xffffffff;
for (size_t i = 0; i < mac.size() * 8; i++) {
uint32 cy = ((crc & 0x80000000U) ? 0x01 : 0x00) ^ (s & 0x01);
crc <<= 1;
s >>= 1;
if (cy) {
crc = (crc ^ CRC_POLY) | cy;
}
}
return crc;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.