デバイスマッピングの実装とインターフェース関数解析

デバイスマッピングの実装において、IOMap構造体は以下の要素で構成される:

typedef struct {
  const char *dev_name;
  paddr_t start_addr;
  paddr_t end_addr;
  void *target_space;
  io_callback_t handler_func;
} DeviceMap;

メモリマッピングとポートマッピングの違い:

  • メモリマッピング: 専用メモリ領域を割当て,CPUの物理メモリアクセスをI/Oデバイスへ転送
  • ポートマッピング: 専用I/O命令でデバイスポートを直接操作

初期化処理ではメモリ領域を確保:

void initialize_mapping_space() {
  device_mem = allocate_memory(DEV_SPACE_SIZE);
  current_ptr = device_mem;
}

タイマーデバイスの登録例:

void setup_timer() {
  timer_reg = reserve_memory(8);
#ifdef PORT_IO_ENABLED
  register_port_map("timer", PORT_ADDR, timer_reg, 8, timer_handler);
#else
  register_mem_map("timer", MEM_ADDR, timer_reg, 8, timer_handler);
#endif
}

メモリマッピング登録時の処理:

void register_mem_map(const char* id, paddr_t base, void* area, uint32_t size, io_callback_t handler) {
  paddr_t range_start = base;
  paddr_t range_end = base + size - 1;
  
  // アドレス重複チェック
  if (address_overlap(range_start, range_end)) {
    report_collision(id, range_start, range_end);
  }

  mapping_table[count] = (DeviceMap){
    .dev_name = id, 
    .start_addr = base, 
    .end_addr = base + size - 1,
    .target_space = area,
    .handler_func = handler
  };
  count++;
}

コールバック関数によるデバイス状態更新:

static void adjust_timer_registers(uint32_t offset, int size, bool write_op) {
  if (!write_op && offset == 4) {
    uint64_t current_time = fetch_current_time();
    timer_reg[0] = (uint32_t)current_time;
    timer_reg[1] = current_time >> 32;
  }
}

デバイスアクセスの核心処理:

static word_t access_mapped_device(paddr_t addr, int len, DeviceMap *map) {
  paddr_t rel_offset = addr - map->start_addr;
  execute_handler(map->handler_func, rel_offset, len, false);
  return read_memory(map->target_space + rel_offset, len);
}

AMフレームワーク側の入出力操作:

static inline uint32_t port_input(uintptr_t loc) { 
  return *(volatile uint32_t *)loc; 
}

static inline void port_output(uintptr_t loc, uint32_t value) { 
  *(volatile uint32_t *)loc = value; 
}

タイマー値取得の実行フロー:

void read_system_uptime(TimeInfo *info) {
  uint32_t time_low = port_input(TIMER_BASE);
  uint32_t time_high = port_input(TIMER_BASE + 4);
  info->microsec = (uint64_t)time_low | ((uint64_t)time_high << 32);
}

全体の実行パス:

  1. AMがport_input()を呼び出し
  2. NEMUがaccess_mapped_device()を介してデバイスアクセス
  3. adjust_timer_registers()でレジスタ値更新
  4. 更新された値がAMに返される

タグ: NEMU AM memory-mapping port-mapping callback-functions

6月25日 23:25 投稿