Upgrade to Pro — share decks privately, control downloads, hide ads and more …

1000 行で実現する Linux on Browser

bokuweb
August 10, 2024

1000 行で実現する Linux on Browser

bokuweb

August 10, 2024
Tweet

More Decks by bokuweb

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ [speaker] name = "bokuweb" guthub = "bokuweb" x =

    "bokuweb17" hobby = [“Ԃܳ”, “։ൃ”] [company] name = "FRAIMגࣜձࣾ"
  2. ࣗݾ঺հ •bokuweb/rustynes 👾 An NES emulator by Rust and WebAssembly

    ˑ472 •bokuweb/gopher-boy 🎮 A Game Boy emulator written in Go ˑ244 •bokuweb/yaw 🦀 A wasm interpreter in Rust ˑ53 •bokuweb/docx-rs 📝 A .docx fi le writer with Rust/WebAssembly ˑ328 bokuweb/r2 A RISC-V emulator written in Rust 🦀 ˑ91 <- new
  3. ࠓճͷϦϙδτϦ bokuweb/r2 A RISC-V emulator written in Rust 🦀 https://github.com/bokuweb/r2

    ࠓ೔ͷ࿩͸ҎԼʹ΄ͱΜͲؚ·Ε·͢ https://github.com/bokuweb/r2/blob/main/core/src/ cpu.rs
  4. RISC-V ͷಛ௃ • Φʔϓϯͳ໋ྩηοτ • Ϟδϡʔϧࣜ • த֩ʹجຊ໋ྩͷRV32I͕͋ΓRV32I͸ౚ݁͞Ε͍ͯΔ • ͜ͷϞδϡʔϧੑʹΑΓඇৗʹখ͍͞ମফඅిྗͷ

    RISC-V Λ ࣮૷͢Δ͜ͱ͕Ͱ͖Δ • ྫ͑͹ RV32IFMD ͸جຊ໋ྩηοτʹ৐ࢉ(M)ɺ୯ਫ਼౓ුಈখ ਺఺(F)ɺഒਫ਼౓ුಈখ਺఺(D)
  5. addi a4, a4 1 (0x0017_0713) ໋ྩͷܗࣜ͸࣍ͷ௨ΓͰ͢ɿaddi rd, rs1, imm •

    rdɿ݁ՌΛॻ͖ࠐΉϨδελʢdestination registerʣ •rs1ɿݩͷ஋Λ࣋ͭϨδελʢsource register 1ʣ •immɿଈ஋ʢimmediate valueʣ rs1 Ϩδελʢ͜͜Ͱ͸ a4ʣͷ஋ΛऔΓग़͠·͢ɻ ଈ஋ immʢ͜͜ Ͱ͸ 1ʣΛऔΓग़͠·͢ɻ rs1 ͷ஋ʹଈ஋ imm ΛՃࢉ͠ɺͦͷ݁Ռ Λ rd Ϩδελʢ͜͜Ͱ͸ a4ʣʹॻ͖ࠐΈ·͢ɻ
  6. ໋ྩܗࣜ 31 25 24 20 19 15 14 12 11

    7 6 0 |-----------|-------|--------|--------|-----------|--------| | funct7 | rs2 | rs1 | funct3 | rd | opcode | Rܗࣜ |-----------|-------|--------|--------|-----------|--------| 31 20 19 15 14 12 11 7 6 0 |-------------------|--------|--------|-----------|--------| | imm[11:0] | rs1 | funct3 | rd | opcode | Iܗࣜ |-------------------|--------|--------|-----------|--------|
  7. ໋ྩܗࣜ 31 25 24 20 19 15 14 12 11

    7 6 0 |------------|------|--------|--------|-----------|--------| | imm[11:5] | rs2 | rs1 | funct3 |imm[4:0] | opcode | Sܗࣜ |------------|------|--------|--------|-----------|--------| 31 25 24 20 19 15 14 12 11 7 6 0 |------------|------|--------|--------|-----------|--------| |imm[12|10:5]| rs2 | rs1 | funct3 |imm[4:1|11]| opcode | Bܗࣜ |------------|------|--------|--------|-----------|--------|
  8. ໋ྩܗࣜ 31 12 11 7 6 0 |-------------------------------------|-----------|--------| | imm[31:12]

    | rd | opcode | Uܗࣜ |-------------------------------------|-----------|--------| 31 20 19 15 14 12 11 7 6 0 |---------------------|------|--------|-----------|--------| | imm[20|10:1|11|19:12] | rd | opcode | Jܗࣜ |---------------------|------|--------|-----------|--------|
  9. ໋ྩܗࣜ addi a5, a4 1ͷ໋ྩܗࣜ͸ҎԼͷIܗࣜ 31 20 19 15 14

    12 11 7 6 0 |-------------------|--------|--------|-----------|--------| | imm[11:0] | rs1 | funct3 | rd | opcode | Iܗࣜ |-------------------|--------|--------|-----------|--------|
  10. addi a4, a4 1 (0x0017_0713) छผ ஋ imm[11:0] 0x01 rs1

    0x0E funct3 0x00 rd 0x0E 0x0017_0713ΛIܗࣜʹ౰ͯ͸ΊͯΈΔͱҎԼͷ͜ͱ͕Θ͔Δ
  11. CPU ͷجຊಈ࡞ جຊతʹ͸ҎԼΛ 1step ͱͯ͠܁Γฦͤ͹Α͍ 1. (PC)ϓϩάϥϜΧ΢ϯλͷࢦ͢ΞυϨε͔Β໋ྩΛಡΉ 2. ໋ྩΛσίʔυ໋͠ྩͷछผ΍ιʔεͱͳΔϨδελͳͲΛ൑ผ͢Δ 3.

    ໋ྩʹ΋ͱ͖ͮԋࢉΛߦ͏ 4. ඞཁʹԠͯ͡ɺϝϞϦ΍ϨδελʹσʔλΛॻ͖໭͢ 5. ϓϩάϥϜΧ΢ϯλΛΠϯΫϦϝϯτ͠ɺ1 ʹ΋ͲΔ
  12. CPU ͷجຊಈ࡞࣮૷Πϝʔδ pub fn start() { loop { step(); }

    } fn step() { // 1. ϓϩάϥϜΧ΢ϯλͷࢦ͢ΞυϨε͔Β໋ྩΛಡΉ // 2. σίʔυ໋͠ྩͷछผ΍ιʔεͱͳΔϨδελͳͲΛ൑ผ͢Δ // 3. ໋ྩʹ΋ͱ͖ͮԋࢉΛߦ͏ // 4. ඞཁʹԠͯ͡ɺϝϞϦ΍ϨδελʹσʔλΛॻ͖໭͢ // 5. ϓϩάϥϜΧ΢ϯλΛΠϯΫϦϝϯτ͠ɺ1 ʹ΋ͲΔ }
  13. Bus pub trait BusReader { fn read8(&self, addr: u32) ->

    Result<u8>; fn read16(&self, addr: u32) -> Result<u16>; fn read32(&self, addr: u32) -> Result<u32>; }
  14. Bus pub struct Bus { pub ram: Vec<u8>, } fn

    read32(&self, addr: u32) -> Result<u32, BusException> { Ok(u32::from_le_bytes([ self.ram[addr as usize], self.ram[addr as usize + 1], self.ram[addr as usize + 2], self.ram[addr as usize + 3], ])) }
  15. Bus RAM ͱ Bus ͷॳظԽΠϝʔδ let ram_size = 640 *

    1024 * 1024; let mut ram = vec![0u8; ram_size] let bus = core::bus::Bus::new(ram, /* ... */);
  16. decode ઌड़ͨ͠Α͏ʹ·ͣ͸ԼҐ7bitΛ֬ೝ͢Δ fn step(&mut self) { // ... match ir

    & 0x7f { 0b0010011=> self.op(ir), _ => todo!("Other instructions"), } }
  17. decode fn op(&mut self, ir: u32) { // funct3Λ൑ผ͢Δ match

    (ir >> 12) & 7 { 0b000 => // Please impl ADDI, _ => todo!("Other instructions"), } }
  18. addi fn op(&mut self, ir: u32) { // ir͔ΒIܗࣜʹ΋ͱ͖ͮrdΛऔΓग़͢ let

    rd = ((ir >> 7) & 0x1f) as usize; // ir͔ΒIܗࣜʹ΋ͱ͖ͮଈ஋ΛऔΓग़͢ let imm = ir >> 20; let imm = imm | if (imm & 0x800) != 0 { 0xfffff000 } else { 0 }; // ir͔ΒIܗࣜʹ΋ͱ͖ͮr͂̍ΛऔΓग़͢ let rs1 = self.x[((ir >> 15) & 0x1f) as usize]; let v = match (ir >> 12) & 7 { // funct3Λ൑ผ͠ɺରԠ͢ΔԋࢉΛߦ͏ɻҎԼ͸Ճࢉɻ 0b000 => rs1.wrapping_add(imm), _ => todo!(), }; if rd != 0 { // write back self.x[rd] = v } }
  19. ͦͷଞجຊ໋ྩ fn op(&mut self, ir: u32) { // ...লུ let

    v = match (ir >> 12) & 7 { 0b000 if reg && (ir & 0x4000_0000) != 0 => rs1.wrapping_sub(rs2), 0b000 => rs1.wrapping_add(rs2), 0b001 => rs1 << (rs2 & 0x1f), 0b010 => ((rs1 as i32) < (rs2 as i32)) as u32, 0b011 => (rs1 < rs2) as u32, 0b100 => rs1 ^ rs2, 0b101 if (ir & 0x40000000) != 0 => ((rs1 as i32) >> (rs2 & 0x1f)) as u32, 0b101 => rs1 >> (rs2 & 0x1f), 0b110 => rs1 | rs2, 0b111 => rs1 & rs2, _ => { self.record_exception(Exception::IllegalInstruction, ir); 0 } }; self.write_back(rd, v) }
  20. load fn load(&mut self, ir: u32) { let rd =

    helpers::rd(ir); let rs1 = self.x[helpers::rs1(ir)]; let imm = ir >> 20; let imm_s = (imm | (if (imm & 0x800) != 0 { 0xfffff000 } else { 0 })) as i32; let rsval = (rs1 as i64 + imm_s as i64) as u32; match (ir >> 12) & 0x7 { 0b010 => match self.bus.read32(rsval) { Ok(v) => self.x[rd] = v, Err(e) => self.record_exception(e.into(), rsval), }, // ...লུ } }
  21. store fn store(&mut self, ir: u32) { let rs1 =

    self.x[helpers::rs1(ir)]; let rs2 = self.x[((ir >> 20) & 0x1f) as usize]; let mut addr = ((ir >> 7) & 0x1f) | ((ir & 0xfe000000) >> 20); if addr & 0x800 != 0 { addr |= 0xfffff000; } let addr = addr.wrapping_add(rs1); match (ir >> 12) & 0x7 { 0b010 => { self.bus .write32(addr, rs2) .unwrap_or_else(|e| self.record_exception(e.into(), addr)); } _ => { self.record_exception(Exception::IllegalInstruction, ir); } }; }
  22. RV32M fn multi_or_div(&mut self, ir: u32) { let rd =

    helpers::rd(ir); let imm = ir >> 20; let imm = imm | if (imm & 0x800) != 0 { 0xfffff000 } else { 0 }; let rs1 = self.x[helpers::rs1(ir)]; let is_reg = (ir & 0b100000) != 0; let rs2 = if is_reg { self.x[imm as usize & 0x1f] } else { imm }; let mut v = 0; match (ir >> 12) & 7 { 0b000 => v = rs1.wrapping_mul(rs2), // MUL 0b001 => v = ((rs1 as i32 as i64).wrapping_mul(rs2 as i32 as i64) >> 32) as u32, // MULH 0b010 => v = ((rs1 as i32 as i64).wrapping_mul(rs2 as i64) >> 32) as u32, // MULHSU 0b011 => v = ((rs1 as u64).wrapping_mul(rs2 as u64) >> 32) as u32, // MULHU 0b100 => v = if rs2 == 0 { !0 } else { (rs1).wrapping_div(rs2) }, // DIV 0b101 => v = if rs2 == 0 { u32::MAX } else { rs1 / rs2 }, // DIVU 0b110 if rs2 == 0 => v = 0, // REM 0b110 => v = (rs1 as i32).wrapping_rem(rs2 as i32) as u32, // REM 0b111 => v = if rs2 == 0 { rs1 } else { rs1 % rs2 }, // REMU _ => { self.record_exception(Exception::IllegalInstruction, ir); } } self.write_back(rd, v); }
  23. RV32A ಛఆͷ൪஍ͷreadɺՃࢉɺॻ͖໭͠Λatomicʹߦ͏໋ྩ fn atomic(&mut self, ir: u32) { // লུ

    let v = match self.bus.read32(rs1) {}; match f { 0b00000 => { rs2 = rs2.wrapping_add(v); self.bus .write32(rs1, rs2) .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); self.write_back(rd, v) } } }
  24. RV32A า͘ಛఆͷ൪஍ΛಡΈग़͠ɺॻ͖ࠐΈ·Ͱʹ֘౰൪஍ʹwrite͕͋ͬͨΒॻ͖໭࣌͠ ʹࣦഊ͢Δ໋ྩ fn atomic(&mut self, ir: u32) { //

    লུ match f { // LR.W // Load-Reserved Word 0b00010 => { self.reserved_load_addresses.insert(rs1, v); self.write_back(rd, v) } // SC.W // Store-Conditional Word 0b00011 => { if let Some(val) = self.reserved_load_addresses.get(&rs1) { if *val == v { self.bus .write32(rs1, rs2) .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); self.write_back(rd, 0); } else { self.write_back(rd, 1); }
  25. Zicsr CSRͷ஋ΛಡΉ fn zicsr(&mut self, ir: u32) { // ...লུ

    if (op & 3) != 0 { let rs1imm = (ir >> 15) & 0x1f; let rs1 = self.x[rs1imm as usize]; let mut val = rs1; match csr { 0x340 => v = self.mscratch, 0x305 => v = self.mtvec, 0x304 => v = self.mie, 0xC00 => v = self.cycle as u32, 0x341 => v = self.mepc, 0x300 => v = self.mstatus, 0x342 => v = self.mcause, 0x343 => v = self.mtval, _ => {} } } else { } self.write_back(rd, v); }
  26. Zicsr CSRʹ஋Λॻ͘ fn zicsr(&mut self, ir: u32) { // ...লུ

    if (op & 3) != 0 { let rs1imm = (ir >> 15) & 0x1f; let rs1 = self.x[rs1imm as usize]; let mut val = rs1; match csr { 0x340 => self.mscratch = val, 0x305 => self.mtvec = val, 0x304 => self.mie = val, 0x344 => self.mip = val, 0x341 => self.mepc = val, 0x300 => self.mstatus = val, 0x342 => self.mcause = val, 0x343 => self.mtval = val, _ => {} } } else { } }
  27. ECALL a7ʹγεςϜίʔϧͷ൪߸ɺa0~ΛҾ਺ʹઃఆͯ͠ECALLΛݺͿ͜ͱͰγ εςშίʔϧΛݺͼग़͢͜ͱ͕Ͱ͖Δ https://git.musl-libc.org/cgit/musl/tree/arch/riscv64/syscall_arch.h fn system(&mut self, ir: u32) {

    let csr = ir >> 20; match csr { 0 if self.previous_mode != PrivilegeMode::User => { self.exception = Exception::EnvironmentCallMmode.into() } // 0x8: Environment call from U-mode 0 => self.exception = Exception::EnvironmentCallUmode.into(), 1 => self.exception = Exception::Breakpoint.into(), _ => self.record_exception(Exception::IllegalInstruction, ir), } }
  28. ྫ֎ॲཧ Exception͕ه࿥͞Ε͍ͯΔ৔߹͸֤stepͷऴΘΓʹׂΓࠐΈ/ྫ֎ॲཧΛߦ͏ fn process_exception(&mut self) { // ׂΓࠐΈ΍ྫ֎ͷཁҼ͸mcauseʹอଘ͢Δ self.mcause =

    self.exception; self.mtval = 0; // mepcʹ͸ׂΓࠐΈ/ྫ֎࣌ͷpcΛอଘ͓ͯ͘͠ self.mepc = self.pc; let prev: u32 = self.previous_mode.into(); // mstatusʹprevious_modeΛอଘ͓ͯ͘͠ self.mstatus = (((self.mstatus) & 0x08) << 4) | (prev << 11); // vector΁δϟϯϓ͢Δ self.pc = self.mtvec; self.previous_mode = PrivilegeMode::Machine; self.exception = 0; }
  29. Exception ҎԼͰmtvecʹhandle_exceptionΛઃఆ͍ͯ͠Δ .align 2 .Lsetup_trap_vector: /* Set trap vector to

    exception handler */ la a0, handle_exception csrw CSR_TVEC, a0 linux/arch/riscv/kernel/head.S
  30. Exception ͜͜Ͱྫ֎tableΛ֬ೝͯ͠ 1: /* Handle other exceptions */ slli t0,

    s4, RISCV_LGPTR la t1, excp_vect_table la t2, excp_vect_table_end add t0, t1, t0 /* Check if exception code lies within bounds */ bgeu t0, t2, 3f REG_L t1, 0(t0) arch/riscv/kernel/entry.S
  31. Exception ҎԼͷςʔϒϧ͔Βdo_trap_ecall_u͕࣮ߦ͞ΕΔ SYM_DATA_START_LOCAL(excp_vect_table) RISCV_PTR do_trap_insn_misaligned ALT_INSN_FAULT(RISCV_PTR do_trap_insn_fault) RISCV_PTR do_trap_insn_illegal RISCV_PTR

    do_trap_break RISCV_PTR do_trap_load_misaligned RISCV_PTR do_trap_load_fault RISCV_PTR do_trap_store_misaligned RISCV_PTR do_trap_store_fault RISCV_PTR do_trap_ecall_u /* system call */ RISCV_PTR do_trap_ecall_s RISCV_PTR do_trap_unknown RISCV_PTR do_trap_ecall_m /* instruciton page fault */ ALT_PAGE_FAULT(RISCV_PTR do_page_fault) RISCV_PTR do_page_fault /* load page fault */ RISCV_PTR do_trap_unknown RISCV_PTR do_page_fault /* store page fault */ SYM_DATA_END_LABEL(excp_vect_table, SYM_L_LOCAL, excp_vect_table_end)
  32. Systemcall void do_trap_ecall_u(struct pt_regs *regs) { if (user_mode(regs)) { long

    syscall = regs->a7; regs->epc += 4; regs->orig_a0 = regs->a0; // ... syscall_handler(regs, syscall); // ... } else { // } } arch/riscv/kernel/traps.c
  33. Systemcall static inline void syscall_handler(struct pt_regs *regs, ulong syscall) {

    syscall_t fn; fn = sys_call_table[syscall]; regs->a0 = fn(regs); }
  34. Timer / CLINT (Core Local Interrupt) CLINT͸MemoryMappedσόΠεͷҰͭ pub struct Clint<T>

    { pub msip: u32, pub mtimecmp: u64, pub mtime: u64, timer: T, } mtime͸ϋʔυ΢ΣΞʹΑΓҰఆपظͰΠϯΫϦϝϯτ͞ ΕΔ 64bit Ϩδελͱͳ͓ͬͯΓɺmtimecmpʹઃఆͨ͠ ஋ΑΓେ͖͘ͳΕ͹ׂΓࠐΈ͕ൃੜ͢Δ
  35. CLINT (Core Local Interrupt) native Ͱ΋ wasm Ͱ΋࢖༻Ͱ͖ΔΑ͏ʹ͢ΔΑ͏ʹ͓ͯ͘͠ͱϒϥ΢β Ͱ΋nativeͰ΋ಈ͔ͤΔ pub

    trait TimerDriver { fn as_micros(&self) -> u64; } struct Elapsed; impl TimerDriver for Elapsed { fn as_micros(&self) -> u64 { unsafe { elapsed_us() as u64 } } }
  36. CLINT (Core Local Interrupt) impl<T: TimerDriver> Clint<T> { pub fn

    step(&mut self, mip: &mut u32) { // ܦա࣌ؒΛՃࢉ͢Δ // ࣮ࡍͷ࣮૷͸ҎԼ // https://github.com/bokuweb/r2/blob/main/devices/src/timer.rs#L14 self.mtime += self.timer.as_micros(); // ׂΓࠐΈൃੜ৚݅Λຬ͍ͨͯͨ͠ΒCPUͷMIPʢbit 7ʣɿMachine timer interrupt pendingΛ ཱͯΔ if self.mtimecmp != 0 && self.mtime >= self.mtimecmp { *mip |= 0x80; } else { *mip &= !(0x80); } } } mtime ͸௨ৗೖྗΫϩοΫΛ෼पͨ͠΋ͷͰΧ΢ϯτΞοϓ͍ͯ͘͠Α͏͕ͩɺࠓճ͸؆ ૉԽͷͨ micro ඵͰΧ΢ϯτΞοϓ͍ͯ͘͠
  37. CLINT (Core Local Interrupt) CPU ͷ step ͷઌ಄ɺ͢ͳΘͪ PC ͔Βͷ

    fetch ͷલʹҎԼͷνΣο ΫΛߦׂ͍ΓࠐΈͷ༗ແΛ֬ೝ͢ΔΑ͏ʹͨ͠ ͜ΕʹΑΓɺtimer ׂΓࠐΈ͕ൃੜ͍ͯ͠Δ৔߹͸ process_exeption ʹΑΓׂΓࠐΈॲ ཧ΁ͱભҠ͢Δ // bit3 in mstatus is MIE: Machine Interrupt Enable if (self.mip & 0x80 != 0) && (self.mie & 0x80 != 0) && (self.mstatus & 0x8 != 0) { self.exception = Interrupt::MachineTimerInterrupt.into(); self.process_exception(); return CpuState::Active; }
  38. Timer let last = performance.now(); const elapsed_us = () =>

    { const elapsed = (performance.now() - last) * 1000; last = performance.now(); return elapsed; };
  39. Timer const imports = { env: { elapsed_us } };

    const instance = await WebAssembly.instantiate(module, imports);
  40. UART fn read8(&self, addr: u32) -> Result<u8, BusException> { match

    addr { // UARTͷ഑ஔ͞ΕͨΞυϨε΁ͷΞΫηεͷΈself.serialʹ഑ૹ // ͲͷΑ͏ʹ͜ͷΞυϨε͕ܾఆ͞ΕΔ͔͸ޙड़ͷDeviceTre 0x10000000..=0x100000ff => { let addr = addr & 0xffff; Ok(self.serial.read(addr)) } _ => { /* ͦͷଞ͸লུ */ } } }
  41. UART fn write8(&mut self, addr: u32, v: u8) -> Result<(),

    BusException> { match addr { // UARTͷ഑ஔ͞ΕͨΞυϨε΁ͷΞΫηεͷΈself.serialʹ഑ૹ͢Δ // ͲͷΑ͏ʹ͜ͷΞυϨε͕ܾఆ͞ΕΔ͔͸ޙड़ͷDeviceTreeʹͯઆ໌͢Δ 0x10000000..=0x100000ff => { let addr = addr & 0xffff; self.serial.write(addr, v as u32); } _ => { /* ͦͷଞ͸লུ */ } }; Ok(()) }
  42. UART extern "C" { fn elapsed_us() -> usize; // ҎԼΛ௥Ճ

    fn tx(s: u32); fn rx() -> u32; fn keydown() -> bool; }
  43. UART impl device_interfaces::SerialInterface for Term { fn read(&self, addr: u32)

    -> u8 { match addr { 0x0000 if unsafe { keydown() } => unsafe { rx() as u8 }, 0x0005 => 0x60 | if unsafe { keydown() } { 1 } else { 0 }, _ => 0, } } }
  44. UART const keybuf = []; self.addEventListener("message", (event) => { keybuf.push(event.data);

    }); const keydown = async () => { await delay(); return !!keybuf.length; };
  45. UART const tx = (s) => { postMessage(s); }; const

    rx = () => { const d = keybuf.shift(); return d.charCodeAt(0); };
  46. UART ϒϥ΢βଆ͸xterm.jsΛ࢖༻ͯ͠ termilal Λ࡞੒͠ɺkey ೖྗ͸ postMessage Ͱ wasm Λ࣮ߦ͍ͯ͠Δ worker

    ʹ௨஌ const term = new Terminal({ fontSize: 12 }); const worker = new Worker("worker.js"); function run() { if (term._initialized) return; term._initialized = true; term.onKey((e) => { worker.postMessage(e.key); }); } run();
  47. Device Tree લड़ͨ͠ UART ͷهड़͸ҎԼɻ͜ͷهड़Ͱ0x10000000൪஍ ʹUARTσόΠε͕഑ஔ͞Ε͍ͯΔ͜ͱΛ఻͑Δ͜ͱ͕Ͱ͖Δ / { ... uart@10000000

    { clock-frequency = <0x1000000>; reg = <0x00 0x10000000 0x00 0x100>; compatible = "ns16850"; }; }
  48. Device Tree CLINT ΋ಉ༷ / { ... clint@11000000 { interrupts-extended

    = <0x02 0x03 0x02 0x07>; reg = <0x00 0x11000000 0x00 0x10000>; compatible = "sifive,clint0\0riscv,clint0"; }; }
  49. Device Tree pub fn start<B: BusController + BusReader + BusWriter>(

    bus: B, pc: u32, dtb_ref: u32, sleep: &dyn Fn(std::time::Duration), ) { 'reboot: { let mut core = Cpu::new(bus); // Pass hart id and ref to dtb. core.a0(0x00) // hart id .a1(dtb_ref) // ref to dtb .pc(pc); loop { /* main loop */} }
  50. Wasm #[no_mangle] pub extern "C" fn start() { let ram_size

    = 640 * 1024 * 1024; let mut ram = vec![0u8; ram_size]; // ࠓճ͸WasmʹຒΊͯ͠·͏ let bin = include_bytes!("../../fixtures/linux.bin"); let dtb = include_bytes!("../../fixtures/default.dtb"); ram[0..bin.len()].copy_from_slice(bin); let dtb_ref = ram_size - dtb.len(); ram[dtb_ref..dtb_ref + dtb.len()].copy_from_slice(dtb); let clint = core::clint::Clint::new(Elapsed); let term = Term; let bus = core::bus::Bus::new(ram, clint, term); let sleep = |_u: std::time::Duration| {}; core::start(bus, RAM_START, dtb_ref as u32 + RAM_START, &sleep); }
  51. Wasm ্هΛҎԼͷΑ͏ʹweb workerͰ instantiate ͯ͠start͢ΔͱCPU ͕૸Γ࢝ΊΔ const imports = {

    env: { elapsed_us, tx, keydown, rx, }, }; WebAssembly.instantiateStreaming(fetch("out.wasm"), imports).then((obj) => obj.instance.exports.start() );
  52. Wasm main thread͔Β͸ҎԼͷΑ͏ʹ࢖༻Ε͹ OK const term = new Terminal({ fontSize:

    12 }); const worker = new Worker("worker.js"); function run() { if (term._initialized) return; term._initialized = true; term.onKey((e) => { worker.postMessage(e.key); }); } run();
  53. Wasm վળҊ 1.Wasm ଆ͔Β͸ CPU Λ 1cycle ͚ͩճؔ͢਺ʢԾʹ step ͱ͢ΔʣΛ

    ࿐ग़͠ɺJS ଆͰ loop Λճ͠ͳ͕Β step Λݺͼͭͭఆظʹ setTimeout ͳͲΛ࢖͍ϚΫϩλεΫΩϡʔΛνΣοΫ͢Δ 2.SharedArrayBuffer Λ࢖͍ɺpostMessage Λ࢖༻ͤͣ key ೖྗͳͲ͸ ௚઀ Memory ΛಡΈॻ͖͢ΔΑ͏ʹ͢Δ 3.Asynsify Λ࢖༻͠ɺkeydown ࣌ʹϚΫϩλεΫΩϡʔΛνΣοΫ͢ Δ
  54. Asynsify ࠓճ͸ 3 ͷ Asynsify ҊΛ࠾༻ ҎԼͷίϚϯυͰࢦఆ ͨؔ͠਺(͜͜Ͱ͸ keydown)Λඇಉظؔ਺ͱͯ͠ wasm

    ʹ౉ͤΔΑ͏ʹͳΔ wasm-opt --asyncify --pass-arg=asyncify- [email protected] ../target/wasm32-unknown- unknown/release/wasm.wasm -o out.wasm
  55. Asynsify ҎԼͷΑ͏ʹ keydown Ͱawait delay()Λ ࣮ࢪ͠ɺMicroTaskQue Λϑϥογϡ͢Δ const keydown =

    async () => { await new Promise(resolve => setTimeout(resolve, 0)); return !!keybuf.length; };
  56. JSPI const keydown = new WebAssembly.Suspending(async () => { await

    new Promise(resolve => setTimeout(resolve, 0)); return !!keybuf.length; }); https://v8.dev/blog/jspi