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

1000 行で実現する Linux on Browser

Avatar for bokuweb bokuweb
August 10, 2024

1000 行で実現する Linux on Browser

Avatar for bokuweb

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