Slide 1

Slide 1 text

1000 ߦͰ࣮ݱ͢Δ Linux on Browser @bokuweb Builderscon2024

Slide 2

Slide 2 text

ࣗݾ঺հ [speaker] name = "bokuweb" guthub = "bokuweb" x = "bokuweb17" hobby = [“Ԃܳ”, “։ൃ”] [company] name = "FRAIMגࣜձࣾ"

Slide 3

Slide 3 text

᳻֯༽ࣃ Platycerium

Slide 4

Slide 4 text

ࣗݾ঺հ •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

Slide 5

Slide 5 text

ຊτʔΫͷ໨ඪ • ʮͳʹͦΕָͦ͠͏΍ͬͯΈͨ ͍ʯͱࢥͬͯ΋Β͏͜ͱ • ࣮ࡍʹ։࢝͢Δࡍͷͱ͔͔ͬΓʹ ͯ͠΋Β͏͜ͱ

Slide 6

Slide 6 text

໨࣍ • RISC-V֓ཁ • Ճࢉ໋ྩΛ࣮૷ͯ͠ΈΔ • ͦͷଞͷ໋ྩͷ࣮૷ • ECALLͱγεςϜίʔϧ • TimerͱUARTͷ࣮૷ • WasmԽͱϒϥ΢βͱͷ࿈ܞ

Slide 7

Slide 7 text

ࠓճͷϦϙδτϦ 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

Slide 8

Slide 8 text

Demo https://bokuweb.github.io/r2/

Slide 9

Slide 9 text

ͳͥ? • 🫱 🫲 Linux ͱ஥ྑ͘ͳΓ͔͔ͨͬͨΒ • 🤩 ָ͍͔͠Β

Slide 10

Slide 10 text

ࠓճͷϓϥϯ • RISC-V RV32IMA,Zifencei,Zicsr32bitΤϛϡϨʔλΛ RustͰ࣮૷ • NOMMUΛલఏͱ͢Δ • ্هΛwasmʹม׵͠ϒϥ΢βͱͷάϧʔίʔυΛ༻ҙ͠ɺϒϥ ΢βͰಈ͔͢ • CLINT(Core Local Interrupt)ɺUARTΛ࣮૷͢Δ • LinuxόΠφϦ͸NOMMUͰbuildࡁͷ΋ͷΛར༻͢Δ

Slide 11

Slide 11 text

RISC-V ͷಛ௃ • Φʔϓϯͳ໋ྩηοτ • Ϟδϡʔϧࣜ • த֩ʹجຊ໋ྩͷRV32I͕͋ΓRV32I͸ౚ݁͞Ε͍ͯΔ • ͜ͷϞδϡʔϧੑʹΑΓඇৗʹখ͍͞ମফඅిྗͷ RISC-V Λ ࣮૷͢Δ͜ͱ͕Ͱ͖Δ • ྫ͑͹ RV32IFMD ͸جຊ໋ྩηοτʹ৐ࢉ(M)ɺ୯ਫ਼౓ුಈখ ਺఺(F)ɺഒਫ਼౓ුಈখ਺఺(D)

Slide 12

Slide 12 text

x86ͷ໋ྩ਺ IUUQTBUNBSLJUJUNFEJBDPKQBJUBSUJDMFTOFXTIUNMΑΓҾ༻

Slide 13

Slide 13 text

RV32IMA ࠓճ͸ RV32IMAΛλʔήοτͱ͢Δ • I: جຊ໋ྩ • M: ৐ࢉٴͼআࢉ • A:ΞτϛοΫ໋ྩ

Slide 14

Slide 14 text

࣮ࡍʹRV32Iͷ؆୯ͳՃࢉ໋ྩ addi a4, a4 1ΛݟͯΈΔ

Slide 15

Slide 15 text

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ʣʹॻ͖ࠐΈ·͢ɻ

Slide 16

Slide 16 text

໋ྩܗࣜ 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ܗࣜ |-------------------|--------|--------|-----------|--------|

Slide 17

Slide 17 text

໋ྩܗࣜ 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ܗࣜ |------------|------|--------|--------|-----------|--------|

Slide 18

Slide 18 text

໋ྩܗࣜ 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ܗࣜ |---------------------|------|--------|-----------|--------|

Slide 19

Slide 19 text

opcodeͷҐஔ͕ڞ௨

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

໋ྩܗࣜ ໋ྩܗࣜΛݟͯΘ͔ΔΑ͏ʹԼҐ 7bit ͕ڞ௨ ͯ͠opcodeͱͳ͍ͬͯΔɻ ͭ·Γɺ·ͣ͸opcodeͷ஋Λௐ΂Ε͹໋ྩܗ ࣜΛߜΓ͜ΊΔΑ͏ʹͳ͍ͬͯΔ

Slide 22

Slide 22 text

addi a4, a4 1 (0x0017_0713) 0x0017_0713͸ԼҐ7bit͸0b001_0011Ͱ͋Γ ϚχϡΞϧΛݟΔͱADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAIͷ͍ͣΕ͔Ͱ͋ Δ͜ͱ͕Θ͔Δɻ

Slide 23

Slide 23 text

addi a4, a4 1 (0x0017_0713) IܗࣜͰ͋Δ͜ͱ͕Θ͔Ε͹ޙ͸funct3ΛݟΔ ͜ͱͰߋʹߜΓ͜Ή͜ͱ͕Ͱ͖ΔΑ͏ʹͳͬ ͍ͯΔ

Slide 24

Slide 24 text

addi a4, a4 1 (0x0017_0713) छผ ஋ imm[11:0] 0x01 rs1 0x0E funct3 0x00 rd 0x0E 0x0017_0713ΛIܗࣜʹ౰ͯ͸ΊͯΈΔͱҎԼͷ͜ͱ͕Θ͔Δ

Slide 25

Slide 25 text

͜ΕΛ࣮૷͍ͯ͘͠

Slide 26

Slide 26 text

؊৺ͷ໋ྩ͸Ͳ͔͜ΒಡΉͷ🤔

Slide 27

Slide 27 text

Minimumͳߏ੒Πϝʔδ

Slide 28

Slide 28 text

PCʢϓϩάϥϜΧ΢ϯλʣͷࢦ͢ ൪஍͔ΒಡΜͰ͘Δ

Slide 29

Slide 29 text

CPU ͷجຊಈ࡞ جຊతʹ͸ҎԼΛ 1step ͱͯ͠܁Γฦͤ͹Α͍ 1. (PC)ϓϩάϥϜΧ΢ϯλͷࢦ͢ΞυϨε͔Β໋ྩΛಡΉ 2. ໋ྩΛσίʔυ໋͠ྩͷछผ΍ιʔεͱͳΔϨδελͳͲΛ൑ผ͢Δ 3. ໋ྩʹ΋ͱ͖ͮԋࢉΛߦ͏ 4. ඞཁʹԠͯ͡ɺϝϞϦ΍ϨδελʹσʔλΛॻ͖໭͢ 5. ϓϩάϥϜΧ΢ϯλΛΠϯΫϦϝϯτ͠ɺ1 ʹ΋ͲΔ

Slide 30

Slide 30 text

CPU ͷجຊಈ࡞࣮૷Πϝʔδ pub fn start() { loop { step(); } } fn step() { // 1. ϓϩάϥϜΧ΢ϯλͷࢦ͢ΞυϨε͔Β໋ྩΛಡΉ // 2. σίʔυ໋͠ྩͷछผ΍ιʔεͱͳΔϨδελͳͲΛ൑ผ͢Δ // 3. ໋ྩʹ΋ͱ͖ͮԋࢉΛߦ͏ // 4. ඞཁʹԠͯ͡ɺϝϞϦ΍ϨδελʹσʔλΛॻ͖໭͢ // 5. ϓϩάϥϜΧ΢ϯλΛΠϯΫϦϝϯτ͠ɺ1 ʹ΋ͲΔ }

Slide 31

Slide 31 text

Ϩδελ x0͔Βx31·Ͱͷ32bitͷϨδελ͕32ݸ͋Δ *ͨͩ͠X0͸0ݻఆ *ઌఔͷa4ͱ͍͏ͷ͸x14

Slide 32

Slide 32 text

minimumͳCPU ͜͜·Ͱͷ৘ใΛ·ͱΊΔͱ࠷খߏ੒͸ҎԼͷΑ͏ʹͳΔ pub struct Cpu { /// CPUόε bus: B, /// Ϩδελ x: [u32; 32], /// ϓϩάϥϜΧ΢ϯλ pc: u32, }

Slide 33

Slide 33 text

Bus pub trait BusReader { fn read8(&self, addr: u32) -> Result; fn read16(&self, addr: u32) -> Result; fn read32(&self, addr: u32) -> Result; }

Slide 34

Slide 34 text

Bus pub struct Bus { pub ram: Vec, } fn read32(&self, addr: u32) -> Result { 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], ])) }

Slide 35

Slide 35 text

Bus RAM ͱ Bus ͷॳظԽΠϝʔδ let ram_size = 640 * 1024 * 1024; let mut ram = vec![0u8; ram_size] let bus = core::bus::Bus::new(ram, /* ... */);

Slide 36

Slide 36 text

fetch ϓϩάϥშΧ΢ϯλ͔Β໋ྩΛಡΜͰ͘Δ fn step(&mut self) { // ↓ ir͕0x0017_0713ͱͯ͠࿩ΛਐΊΔ let Ok(ir) = self.bus.read32(self.pc) else { /* handle error */ }; }

Slide 37

Slide 37 text

decode ઌड़ͨ͠Α͏ʹ·ͣ͸ԼҐ7bitΛ֬ೝ͢Δ fn step(&mut self) { // ... match ir & 0x7f { 0b0010011=> self.op(ir), _ => todo!("Other instructions"), } }

Slide 38

Slide 38 text

decode fn op(&mut self, ir: u32) { // funct3Λ൑ผ͢Δ match (ir >> 12) & 7 { 0b000 => // Please impl ADDI, _ => todo!("Other instructions"), } }

Slide 39

Slide 39 text

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 } }

Slide 40

Slide 40 text

͜ΕͰଈ஋ͷՃࢉͷΈ ߦ͑ΔCPU͸׬੒

Slide 41

Slide 41 text

ͦͷଞجຊ໋ྩ 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) }

Slide 42

Slide 42 text

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), }, // ...লུ } }

Slide 43

Slide 43 text

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); } }; }

Slide 44

Slide 44 text

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); }

Slide 45

Slide 45 text

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) } } }

Slide 46

Slide 46 text

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); }

Slide 47

Slide 47 text

Zicsr CSR͸Control and statusϨδελͱ͍ͬͯɺݱࡏͷCPUͷ αΠΫϧ਺΍ׂΓࠐΈʹ࢖༻͢Δ৘ใͳͲΛѻ͏Ϩδελ Zicsr͸CSRΛಡΈॻ͖͢ΔͨΊͷઐ༻໋ྩ

Slide 48

Slide 48 text

Zicsr Ұ෦Ͱ͸͋Δ͕ҎԼͷΑ͏ͳϨδελΛCPUʹ௥Ճ͢Δ pub struct Cpu { // ...লུ mtvec: u32, mie: u32, mip: u32, mepc: u32, mtval: u32, }

Slide 49

Slide 49 text

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); }

Slide 50

Slide 50 text

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 { } }

Slide 51

Slide 51 text

ྫ֎ͷه࿥ ໋֤ྩͰྫ֎͕͋ͬͨ৔߹͸record_exceptionͰྫ֎͕ىͬͨ͜ ͜ͱΛه࿥͓ͯ͘͠(ৄࡉ͸ʮྫ֎ॲཧʯʹͯޙड़) fn record_exception(&mut self, e: Exception, cause: u32) { self.exception = e.into(); self.cause = cause; }

Slide 52

Slide 52 text

ྫ֎ίʔυ ྫ֎ίʔυΛmcasueͱ͍͏Ϩδελʹ֨ೲ͢Δඞཁ͕͋Δ
 ࠷্ҐϏοτͰׂΓࠐΈ͔ྫ֎͔Λ൑ఆ͢Δ

Slide 53

Slide 53 text

͜ΕͰ͍͍ͩͨͷ໋ྩ͸ ࣮૷׬ྃ

Slide 54

Slide 54 text

γεςϜίʔϧͬͯͲ͏ ͳͬͯΜͷ🤔

Slide 55

Slide 55 text

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), } }

Slide 56

Slide 56 text

ྫ֎ॲཧ 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; }

Slide 57

Slide 57 text

Linux͕Ͳ͏ͯ͠Δ͔ݟͯ ΈΔ

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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)

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Systemcall static inline void syscall_handler(struct pt_regs *regs, ulong syscall) { syscall_t fn; fn = sys_call_table[syscall]; regs->a0 = fn(regs); }

Slide 63

Slide 63 text

͋ͱ͸Timerͷ༻ҙ͕ඞཁ

Slide 64

Slide 64 text

Timer / CLINT (Core Local Interrupt) CLINT͸MemoryMappedσόΠεͷҰͭ pub struct Clint { pub msip: u32, pub mtimecmp: u64, pub mtime: u64, timer: T, } mtime͸ϋʔυ΢ΣΞʹΑΓҰఆपظͰΠϯΫϦϝϯτ͞ ΕΔ 64bit Ϩδελͱͳ͓ͬͯΓɺmtimecmpʹઃఆͨ͠ ஋ΑΓେ͖͘ͳΕ͹ׂΓࠐΈ͕ൃੜ͢Δ

Slide 65

Slide 65 text

CLINT (Core Local Interrupt) MemoryMappedσόΠεͳͷͰBusʹҎԼͷΑ͏ʹ௥Ճ pub struct Bus { pub ram: Vec, pub clint: Clint, }

Slide 66

Slide 66 text

Timer / CLINT (Core Local Interrupt)

Slide 67

Slide 67 text

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 } } }

Slide 68

Slide 68 text

CLINT (Core Local Interrupt) impl Clint { 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 ඵͰΧ΢ϯτΞοϓ͍ͯ͘͠

Slide 69

Slide 69 text

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; }

Slide 70

Slide 70 text

ͲͷΑ͏ʹϒϥ΢βͱ઀ଓ ͢Δ͔

Slide 71

Slide 71 text

Timer extern "C" { fn elapsed_us() -> usize; }

Slide 72

Slide 72 text

Timer let last = performance.now(); const elapsed_us = () => { const elapsed = (performance.now() - last) * 1000; last = performance.now(); return elapsed; };

Slide 73

Slide 73 text

Timer const imports = { env: { elapsed_us } }; const instance = await WebAssembly.instantiate(module, imports);

Slide 74

Slide 74 text

͜͜·ͰͰLinux͸ಈ͘͸ͣ ʢ͕؍ଌ͢Δ͢΂͕ͳ͍ʣ

Slide 75

Slide 75 text

UART Universal Asynchronous Receiver Transmitter

Slide 76

Slide 76 text

UART UART ΋·ͨɺϝϞϦϚοϓτͳ֎෦σόΠεͰ ͋ΔͨΊ Bus Λհͯ͠ΞΫηε͢ΔͨΊҎԼͷΑ ͏ʹ Bus ʹ࣮ମΛ͍࣋ͨͤͯΔ pub struct Bus { pub ram: Vec, pub clint: Clint, pub serial: S, }

Slide 77

Slide 77 text

UART

Slide 78

Slide 78 text

UART UART΋MemoryMappedͳσόΠεͰ͋Δͨ Ί CPU͔ΒݟͨΒread/write ͕Ͱ͖Ε͹Α͍ pub trait SerialInterface { fn read(&self, addr: u32) -> u8; fn write(&self, addr: u32, v: u32); }

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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(()) }

Slide 81

Slide 81 text

UART extern "C" { fn elapsed_us() -> usize; // ҎԼΛ௥Ճ fn tx(s: u32); fn rx() -> u32; fn keydown() -> bool; }

Slide 82

Slide 82 text

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, } } }

Slide 83

Slide 83 text

UART const keybuf = []; self.addEventListener("message", (event) => { keybuf.push(event.data); }); const keydown = async () => { await delay(); return !!keybuf.length; };

Slide 84

Slide 84 text

UART const tx = (s) => { postMessage(s); }; const rx = () => { const d = keybuf.shift(); return d.charCodeAt(0); };

Slide 85

Slide 85 text

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();

Slide 86

Slide 86 text

Device Tree ͜͜·Ͱ CLINT ΍ UART ͳͲ MemoryMapped ͳσόΠε͕Ͳͷ൪஍ʹ഑ஔ ͞ΕΔͷ͔ʁ Ϙʔυ΍ rev ʹΑͬͯ͜ͷ൪஍ ͸มΘΓ͏ΔͷͰ͸🤔ʁ

Slide 87

Slide 87 text

Device Tree ͜ΕΛղܾ͢Δͷ͕Device Tree ਓ͕ؒಡΈॻ͖Ͱ͖Δdtsͱ͍͏ϑΥʔ ϚοτͰهड़͠ɺdtbʹίϯύΠϧ͢Δ

Slide 88

Slide 88 text

Device Tree ͨͱ͑͹ RAM ͷྖҬͱαΠζΛࢦఆ͢Δͷʹ͸ҎԼͷΑ͏ʹ هड़͢Δ͜ΕͰ0x80000000൪஍͔ΒαΠζ0x3ffc000ͷRAM ྖҬ͕͋Δ͜ͱΛ fi emwareʹڭ͑Δ͜ͱ͕Ͱ͖Δ / { ... memory@80000000 { device_type = "memory"; reg = <0x00 0x80000000 0x00 0x3ffc000>; }; }

Slide 89

Slide 89 text

Device Tree લड़ͨ͠ UART ͷهड़͸ҎԼɻ͜ͷهड़Ͱ0x10000000൪஍ ʹUARTσόΠε͕഑ஔ͞Ε͍ͯΔ͜ͱΛ఻͑Δ͜ͱ͕Ͱ͖Δ / { ... uart@10000000 { clock-frequency = <0x1000000>; reg = <0x00 0x10000000 0x00 0x100>; compatible = "ns16850"; }; }

Slide 90

Slide 90 text

Device Tree CLINT ΋ಉ༷ / { ... clint@11000000 { interrupts-extended = <0x02 0x03 0x02 0x07>; reg = <0x00 0x11000000 0x00 0x10000>; compatible = "sifive,clint0\0riscv,clint0"; }; }

Slide 91

Slide 91 text

Device Tree dtc -O dtb -o example.dtb ./ fixtures/linux.dts

Slide 92

Slide 92 text

Device Tree pub fn start( 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 */} }

Slide 93

Slide 93 text

Device Tree

Slide 94

Slide 94 text

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); }

Slide 95

Slide 95 text

Wasm cargo build --target wasm32- unknown-unknown --release

Slide 96

Slide 96 text

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() );

Slide 97

Slide 97 text

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();

Slide 98

Slide 98 text

wasm ͜ΕͰಈ͘͸ͣ… ͕Ωʔೖྗ͕Ͱ͖ͳ͍

Slide 99

Slide 99 text

Wasm obj.instance.exports.start()Ͱwasm ଆͰແݶϧʔϓʹೖΔͷͰϚΫϩλεΫΩϡʔ ͕ࡹ͚ͣ key ೖྗ͕ wasm ଆͰड͚औΕͳ͍..

Slide 100

Slide 100 text

Wasm վળҊ 1.Wasm ଆ͔Β͸ CPU Λ 1cycle ͚ͩճؔ͢਺ʢԾʹ step ͱ͢ΔʣΛ ࿐ग़͠ɺJS ଆͰ loop Λճ͠ͳ͕Β step Λݺͼͭͭఆظʹ setTimeout ͳͲΛ࢖͍ϚΫϩλεΫΩϡʔΛνΣοΫ͢Δ 2.SharedArrayBuffer Λ࢖͍ɺpostMessage Λ࢖༻ͤͣ key ೖྗͳͲ͸ ௚઀ Memory ΛಡΈॻ͖͢ΔΑ͏ʹ͢Δ 3.Asynsify Λ࢖༻͠ɺkeydown ࣌ʹϚΫϩλεΫΩϡʔΛνΣοΫ͢ Δ

Slide 101

Slide 101 text

Asynsify ࠓճ͸ 3 ͷ Asynsify ҊΛ࠾༻ ҎԼͷίϚϯυͰࢦఆ ͨؔ͠਺(͜͜Ͱ͸ keydown)Λඇಉظؔ਺ͱͯ͠ wasm ʹ౉ͤΔΑ͏ʹͳΔ wasm-opt --asyncify --pass-arg=asyncify- [email protected] ../target/wasm32-unknown- unknown/release/wasm.wasm -o out.wasm

Slide 102

Slide 102 text

Asynsify ҎԼͷΑ͏ʹ keydown Ͱawait delay()Λ ࣮ࢪ͠ɺMicroTaskQue Λϑϥογϡ͢Δ const keydown = async () => { await new Promise(resolve => setTimeout(resolve, 0)); return !!keybuf.length; };

Slide 103

Slide 103 text

JSPI const keydown = new WebAssembly.Suspending(async () => { await new Promise(resolve => setTimeout(resolve, 0)); return !!keybuf.length; }); https://v8.dev/blog/jspi

Slide 104

Slide 104 text

·ͱΊ • RISC-VͰ͋Ε͹͔ͳΓϋʔυ΢ΣΞػೳߜͬ ͨ࠷খݶͷ࣮૷ͰLinuxΛಈ͔͢͜ͱ͕Ͱ͖Δ • 1000ߦલޙͷ࣮૷ʹऩ·ΔͷͰ୹ظͷ॓୊ͱ ͯ͠࠷ద • CPU΍ϋʔυ΢ΣΞͷಈ͖ΛֶͿͷʹྑ͍୊ࡐ

Slide 105

Slide 105 text

͜ΕΛϕʔεʹ • ࣗ࡞OSΛߏஙͯ͠ΈΔ • https://operating-system-in-1000-lines.vercel.app/ja/assembly • xv6ΛҠ২ͯ͠ΈΔ • MMU͋ΓLinux͕ಈ͘Α͏ʹͯ͠ΈΔ • FPGAϘʔυʹҠ২ͯ͠ΈΔ • ωοτϫʔΫ͕ಈ͘Α͏ʹͯ͠ΈΔ

Slide 106

Slide 106 text

Let’s enjoy build your own X