デバイスマッピングの実装において、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);
}
全体の実行パス:
- AMがport_input()を呼び出し
- NEMUがaccess_mapped_device()を介してデバイスアクセス
- adjust_timer_registers()でレジスタ値更新
- 更新された値がAMに返される