diff --git a/README.md b/README.md index 7c5d6bd9..35f5a356 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Going to be the next generation teaching operating system. Supported architectures: x86_64, RISCV32/64, AArch64, MIPS32 -Tested boards: QEMU, HiFive Unleashed, x86_64 PC (i5/i7), Raspberry Pi 3B+ +Tested boards: QEMU, HiFive Unleashed, x86_64 PC (i5/i7), Raspberry Pi 3B+, Kendryte K210 and FPGA running Rocket Chip ![demo](./docs/2_OSLab/os2atc/demo.png) diff --git a/crate/memory/src/memory_set/handler/byframe.rs b/crate/memory/src/memory_set/handler/byframe.rs index e7f90db0..f01eca61 100644 --- a/crate/memory/src/memory_set/handler/byframe.rs +++ b/crate/memory/src/memory_set/handler/byframe.rs @@ -22,6 +22,20 @@ impl MemoryHandler for ByFrame { pt.unmap(addr); } + fn clone_map( + &self, + pt: &mut PageTable, + with: &Fn(&mut FnMut()), + addr: VirtAddr, + attr: &MemoryAttr, + ) { + let data = Vec::from(pt.get_page_slice_mut(addr)); + with(&mut || { + self.map(pt, addr, attr); + pt.get_page_slice_mut(addr).copy_from_slice(&data); + }); + } + fn handle_page_fault(&self, _pt: &mut PageTable, _addr: VirtAddr) -> bool { false } diff --git a/crate/memory/src/memory_set/handler/delay.rs b/crate/memory/src/memory_set/handler/delay.rs index fd7b9d37..ac4e856f 100644 --- a/crate/memory/src/memory_set/handler/delay.rs +++ b/crate/memory/src/memory_set/handler/delay.rs @@ -16,13 +16,6 @@ impl MemoryHandler for Delay { attr.apply(entry); } - fn map_eager(&self, pt: &mut PageTable, addr: VirtAddr, attr: &MemoryAttr) { - let target = self.allocator.alloc().expect("failed to alloc frame"); - let entry = pt.map(addr, target); - entry.set_present(true); - attr.apply(entry); - } - fn unmap(&self, pt: &mut PageTable, addr: VirtAddr) { let entry = pt.get_entry(addr).expect("failed to get entry"); if entry.present() { @@ -34,6 +27,30 @@ impl MemoryHandler for Delay { pt.unmap(addr); } + fn clone_map( + &self, + pt: &mut PageTable, + with: &Fn(&mut FnMut()), + addr: VirtAddr, + attr: &MemoryAttr, + ) { + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + // eager map and copy data + let data = Vec::from(pt.get_page_slice_mut(addr)); + with(&mut || { + let target = self.allocator.alloc().expect("failed to alloc frame"); + let target_data = pt.get_page_slice_mut(addr); + let entry = pt.map(addr, target); + target_data.copy_from_slice(&data); + attr.apply(entry); + }); + } else { + // delay map + with(&mut || self.map(pt, addr, attr)); + } + } + fn handle_page_fault(&self, pt: &mut PageTable, addr: VirtAddr) -> bool { let entry = pt.get_entry(addr).expect("failed to get entry"); if entry.present() { @@ -44,6 +61,11 @@ impl MemoryHandler for Delay { entry.set_target(frame); entry.set_present(true); entry.update(); + //init with zero for delay mmap mode + let data = pt.get_page_slice_mut(addr); + for x in data { + *x = 0; + } true } } diff --git a/crate/memory/src/memory_set/handler/file.rs b/crate/memory/src/memory_set/handler/file.rs new file mode 100644 index 00000000..09f0acb5 --- /dev/null +++ b/crate/memory/src/memory_set/handler/file.rs @@ -0,0 +1,108 @@ +use super::*; + +/// Delay mapping a page to an area of a file. +#[derive(Clone)] +pub struct File { + pub file: F, + pub mem_start: usize, + pub file_start: usize, + pub file_end: usize, + pub allocator: T, +} + +pub trait Read: Clone + Send + Sync + 'static { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize; +} + +impl MemoryHandler for File { + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn map(&self, pt: &mut PageTable, addr: usize, attr: &MemoryAttr) { + let entry = pt.map(addr, 0); + entry.set_present(false); + attr.apply(entry); + } + + fn unmap(&self, pt: &mut PageTable, addr: usize) { + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + self.allocator.dealloc(entry.target()); + } + + // PageTable::unmap requires page to be present + entry.set_present(true); + pt.unmap(addr); + } + + fn clone_map( + &self, + pt: &mut PageTable, + with: &Fn(&mut FnMut()), + addr: usize, + attr: &MemoryAttr, + ) { + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() && !attr.readonly { + // eager map and copy data + let data = Vec::from(pt.get_page_slice_mut(addr)); + with(&mut || { + let target = self.allocator.alloc().expect("failed to alloc frame"); + let target_data = pt.get_page_slice_mut(addr); + let entry = pt.map(addr, target); + target_data.copy_from_slice(&data); + attr.apply(entry); + }); + } else { + // delay map + with(&mut || self.map(pt, addr, attr)); + } + } + + fn handle_page_fault(&self, pt: &mut PageTable, addr: usize) -> bool { + let addr = addr & !(PAGE_SIZE - 1); + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + return false; + } + let frame = self.allocator.alloc().expect("failed to alloc frame"); + entry.set_target(frame); + entry.set_present(true); + let writable = entry.writable(); + entry.set_writable(true); + entry.update(); + + self.fill_data(pt, addr); + + let entry = pt.get_entry(addr).expect("failed to get entry"); + entry.set_writable(writable); + entry.update(); + + true + } +} + +impl File { + fn fill_data(&self, pt: &mut PageTable, addr: VirtAddr) { + let data = pt.get_page_slice_mut(addr); + let file_offset = addr + self.file_start - self.mem_start; + let read_size = (self.file_end as isize - file_offset as isize) + .min(PAGE_SIZE as isize) + .max(0) as usize; + let read_size = self.file.read_at(file_offset, &mut data[..read_size]); + if read_size != PAGE_SIZE { + data[read_size..].iter_mut().for_each(|x| *x = 0); + } + } +} + +impl Debug for File { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.debug_struct("FileHandler") + .field("mem_start", &self.mem_start) + .field("file_start", &self.file_start) + .field("file_end", &self.file_end) + .finish() + } +} diff --git a/crate/memory/src/memory_set/handler/linear.rs b/crate/memory/src/memory_set/handler/linear.rs index 1e10b90f..1645f91f 100644 --- a/crate/memory/src/memory_set/handler/linear.rs +++ b/crate/memory/src/memory_set/handler/linear.rs @@ -20,6 +20,16 @@ impl MemoryHandler for Linear { pt.unmap(addr); } + fn clone_map( + &self, + pt: &mut PageTable, + with: &Fn(&mut FnMut()), + addr: VirtAddr, + attr: &MemoryAttr, + ) { + with(&mut || self.map(pt, addr, attr)); + } + fn handle_page_fault(&self, _pt: &mut PageTable, _addr: VirtAddr) -> bool { false } diff --git a/crate/memory/src/memory_set/handler/mod.rs b/crate/memory/src/memory_set/handler/mod.rs index fd6602c3..9a17732b 100644 --- a/crate/memory/src/memory_set/handler/mod.rs +++ b/crate/memory/src/memory_set/handler/mod.rs @@ -1,23 +1,28 @@ use super::*; // here may be a interesting part for lab -pub trait MemoryHandler: Debug + 'static { +pub trait MemoryHandler: Debug + Send + Sync + 'static { fn box_clone(&self) -> Box; /// Map `addr` in the page table /// Should set page flags here instead of in page_fault_handler fn map(&self, pt: &mut PageTable, addr: VirtAddr, attr: &MemoryAttr); - /// Map `addr` in the page table eagerly (i.e. no delay allocation) - /// Should set page flags here instead of in page_fault_handler - fn map_eager(&self, pt: &mut PageTable, addr: VirtAddr, attr: &MemoryAttr) { - // override this when pages are allocated lazily - self.map(pt, addr, attr); - } - /// Unmap `addr` in the page table fn unmap(&self, pt: &mut PageTable, addr: VirtAddr); + /// Clone map `addr` from one page table to another. + /// `pt` is the current active page table. + /// `with` is the `InactivePageTable::with` function. + /// Call `with` then use `pt` as target page table inside. + fn clone_map( + &self, + pt: &mut PageTable, + with: &Fn(&mut FnMut()), + addr: VirtAddr, + attr: &MemoryAttr, + ); + /// Handle page fault on `addr` /// Return true if success, false if error fn handle_page_fault(&self, pt: &mut PageTable, addr: VirtAddr) -> bool; @@ -29,16 +34,18 @@ impl Clone for Box { } } -pub trait FrameAllocator: Debug + Clone + 'static { +pub trait FrameAllocator: Debug + Clone + Send + Sync + 'static { fn alloc(&self) -> Option; fn dealloc(&self, target: PhysAddr); } mod byframe; mod delay; +mod file; mod linear; //mod swap; pub use self::byframe::ByFrame; pub use self::delay::Delay; +pub use self::file::{File, Read}; pub use self::linear::Linear; diff --git a/crate/memory/src/memory_set/mod.rs b/crate/memory/src/memory_set/mod.rs index 94d87650..9eb566a7 100644 --- a/crate/memory/src/memory_set/mod.rs +++ b/crate/memory/src/memory_set/mod.rs @@ -3,6 +3,7 @@ use alloc::{boxed::Box, string::String, vec::Vec}; use core::fmt::{Debug, Error, Formatter}; +use core::mem::size_of; use crate::paging::*; @@ -23,8 +24,6 @@ pub struct MemoryArea { name: &'static str, } -unsafe impl Send for MemoryArea {} - impl MemoryArea { /* ** @brief get slice of the content in the memory area @@ -54,15 +53,27 @@ impl MemoryArea { pub fn contains(&self, addr: VirtAddr) -> bool { addr >= self.start_addr && addr < self.end_addr } - /// Check the array is within the readable memory - fn check_read_array(&self, ptr: *const S, count: usize) -> bool { + /// Check the array is within the readable memory. + /// Return the size of space covered in the area. + fn check_read_array(&self, ptr: *const S, count: usize) -> usize { // page align - ptr as usize >= Page::of_addr(self.start_addr).start_address() - && unsafe { ptr.add(count) as usize } < Page::of_addr(self.end_addr + PAGE_SIZE - 1).start_address() + let min_bound = (ptr as usize).max(Page::of_addr(self.start_addr).start_address()); + let max_bound = unsafe { ptr.add(count) as usize } + .min(Page::of_addr(self.end_addr + PAGE_SIZE - 1).start_address()); + if max_bound >= min_bound { + max_bound - min_bound + } else { + 0 + } } - /// Check the array is within the writable memory - fn check_write_array(&self, ptr: *mut S, count: usize) -> bool { - !self.attr.readonly && self.check_read_array(ptr, count) + /// Check the array is within the writable memory. + /// Return the size of space covered in the area. + fn check_write_array(&self, ptr: *mut S, count: usize) -> usize { + if self.attr.readonly { + 0 + } else { + self.check_read_array(ptr, count) + } } /// Check the null-end C string is within the readable memory, and is valid. /// If so, clone it to a String. @@ -86,31 +97,13 @@ impl MemoryArea { let p3 = Page::of_addr(end_addr - 1) + 1; !(p1 <= p2 || p0 >= p3) } - /* - ** @brief map the memory area to the physice address in a page table - ** @param pt: &mut T::Active the page table to use - ** @retval none - */ + /// Map all pages in the area to page table `pt` fn map(&self, pt: &mut PageTable) { for page in Page::range_of(self.start_addr, self.end_addr) { self.handler.map(pt, page.start_address(), &self.attr); } } - /* - ** @brief map the memory area to the physice address in a page table eagerly - ** @param pt: &mut T::Active the page table to use - ** @retval none - */ - fn map_eager(&self, pt: &mut PageTable) { - for page in Page::range_of(self.start_addr, self.end_addr) { - self.handler.map_eager(pt, page.start_address(), &self.attr); - } - } - /* - ** @brief unmap the memory area from the physice address in a page table - ** @param pt: &mut T::Active the page table to use - ** @retval none - */ + /// Unmap all pages in the area from page table `pt` fn unmap(&self, pt: &mut PageTable) { for page in Page::range_of(self.start_addr, self.end_addr) { self.handler.unmap(pt, page.start_address()); @@ -204,28 +197,60 @@ impl MemorySet { } } /// Check the pointer is within the readable memory - pub fn check_read_ptr(&self, ptr: *const S) -> VMResult<()> { - self.check_read_array(ptr, 1) + pub unsafe fn check_read_ptr(&self, ptr: *const S) -> VMResult<&'static S> { + self.check_read_array(ptr, 1).map(|s| &s[0]) } /// Check the pointer is within the writable memory - pub fn check_write_ptr(&self, ptr: *mut S) -> VMResult<()> { - self.check_write_array(ptr, 1) + pub unsafe fn check_write_ptr(&self, ptr: *mut S) -> VMResult<&'static mut S> { + self.check_write_array(ptr, 1).map(|s| &mut s[0]) } /// Check the array is within the readable memory - pub fn check_read_array(&self, ptr: *const S, count: usize) -> VMResult<()> { - self.areas - .iter() - .find(|area| area.check_read_array(ptr, count)) - .map(|_| ()) - .ok_or(VMError::InvalidPtr) + pub unsafe fn check_read_array( + &self, + ptr: *const S, + count: usize, + ) -> VMResult<&'static [S]> { + let mut valid_size = 0; + for area in self.areas.iter() { + valid_size += area.check_read_array(ptr, count); + if valid_size == size_of::() * count { + return Ok(core::slice::from_raw_parts(ptr, count)); + } + } + Err(VMError::InvalidPtr) } /// Check the array is within the writable memory - pub fn check_write_array(&self, ptr: *mut S, count: usize) -> VMResult<()> { - self.areas - .iter() - .find(|area| area.check_write_array(ptr, count)) - .map(|_| ()) - .ok_or(VMError::InvalidPtr) + pub unsafe fn check_write_array( + &self, + ptr: *mut S, + count: usize, + ) -> VMResult<&'static mut [S]> { + let mut valid_size = 0; + for area in self.areas.iter() { + valid_size += area.check_write_array(ptr, count); + if valid_size == size_of::() * count { + return Ok(core::slice::from_raw_parts_mut(ptr, count)); + } + } + Err(VMError::InvalidPtr) + } + /// Check the null-end C string pointer array + /// Used for getting argv & envp + pub unsafe fn check_and_clone_cstr_array( + &self, + mut argv: *const *const u8, + ) -> VMResult> { + let mut args = Vec::new(); + loop { + let cstr = *self.check_read_ptr(argv)?; + if cstr.is_null() { + break; + } + let arg = self.check_and_clone_cstr(cstr)?; + args.push(arg); + argv = argv.add(1); + } + Ok(args) } /// Check the null-end C string is within the readable memory, and is valid. /// If so, clone it to a String. @@ -283,7 +308,15 @@ impl MemorySet { name, }; self.page_table.edit(|pt| area.map(pt)); - self.areas.push(area); + // keep order by start address + let idx = self + .areas + .iter() + .enumerate() + .find(|(_, other)| start_addr < other.start_addr) + .map(|(i, _)| i) + .unwrap_or(self.areas.len()); + self.areas.insert(idx, area); } /* @@ -474,20 +507,29 @@ impl MemorySet { None => false, } } -} -impl Clone for MemorySet { - fn clone(&self) -> Self { - let mut page_table = T::new(); + pub fn clone(&mut self) -> Self { + let new_page_table = T::new(); + let Self { + ref mut page_table, + ref areas, + .. + } = self; page_table.edit(|pt| { - // without CoW, we should allocate the pages eagerly - for area in self.areas.iter() { - area.map_eager(pt); + for area in areas.iter() { + for page in Page::range_of(area.start_addr, area.end_addr) { + area.handler.clone_map( + pt, + &|f| unsafe { new_page_table.with(f) }, + page.start_address(), + &area.attr, + ); + } } }); MemorySet { - areas: self.areas.clone(), - page_table, + areas: areas.clone(), + page_table: new_page_table, } } } diff --git a/docs/rcore-structures.drawio b/docs/rcore-structures.drawio new file mode 100644 index 00000000..602cca09 --- /dev/null +++ b/docs/rcore-structures.drawio @@ -0,0 +1 @@ +7V1bc6M6Ev4t++Cq5CEu7uDHJDM5u7XJTiqec3tyEVu22WBgBZ4k59evJCRuEjZ2EMycUSo1E4QAof7UdH9qtSbm7e7tF+gn24d4BcKJoa3eJuaniWHouu6g/3DJOy2xXVqygcGKlpUF8+AvQAs1WroPViCtVcziOMyCpF64jKMILLNamQ9h/Fqvto7D+lMTfwO4gvnSD/nS34NVtmUvpmnliX+CYLOlj/ZseuLZX75sYLyP6PMmhrkmP/npnc/uReunW38Vv1aKzM8T8xbGcZb/tXu7BSHuXNZt+XV3LWeLdkMQZV0uMENrD93Zqx8tzLvb7Zsf31tXhp3f5psf7mmHPMJ4CdI0hhPzGv3CZQzBVbaFwEc3uk7Y2SlM6Xtl76wv09dgF/oROrpZx1E2p2c0dLzcBuHq3n+P97ixaYY6jx3dbGMY/IXq+yE6paMCdBpmFCqGg+8WhOFtHOI2fYpi8oDyojm+GX0MBCm67JF1it4oevDfahXv/TRjDYzD0E/S4Jk0GV+48+EmiG7iLIt3tBLtLAAz8NYqBr0QLho1IN6BDL6jKvQC06F4YAPGs/Lj1xJ9xeDYVoBnGxT0FPCb4tal0NEfVO6nYMBpwUAM/4XGHOTEjF4+I1KC8QtoiEUgKT8MNhE6DMEaX4Z7L0Dj75oWZ3GCb5b4yyDa3JM6n6yy5In2AC6K0bXrkAyibbBagQgLMs78zH8ugJbEQZSRLrJv0C/qyFttak9s1PBbdKyXx+gXV4fZbRyhd/EDIkCAIPEKMCy6SfvAsOIxQGXOevyYyFm9/kU+ax/2uczV2Jcw9h2jMfZtjweCJQCCbmqSkMBuXEECrn+Niva4l9TgP33wz04e/CKZSxv8ps6JHI/sXOgXX9G1qLMM7SZ+mxhOiPsC9RERu+Fs8OGlAkXfoGAf9/FAYXCgCOM4WSyp6Ak42iGhENE3ItyOnwZpiLD4L8POj5A3BHMwIOuAgeErsRAekdum8CALD7om8BOGVREmJ1WwQr40PUSdso03ceSHn8vSG+IggxXt6LLOfYxFSGyt/4Ise6cWn7/PYmzgZTtmD4K3IPuj8vef+FZIGPnRJ2bTkYN3dhCh98UXXWlTTbNZSX6pbrHj8mJyVLv6EcAAdRsGe8X6w+972PZD3RPv4RIcEjQVEDJ0N+AcREAQ+lnwrd6S/sXNUwNsvKNhFpUjPS97ZgWPT19uP8/nX57muZ5AA6hCKaD62sP1H4vbx18X//n1gYwxegPI7kD9DNzqeEV8jWYNVoKqPAvK6u1rYLaCrR5MerODNz8TjFJT2ijlvflfwvjZxxf+5sMAqz3ee6PqudI3p2rqJeowPFo4Xb1DWpeogtdtkIE50tD4ma/QTzj10IM8ZnV5eDYnDk+kM6W5V65gEDWVaLS6xkRm2acrP92STtEbupDpNW3qaVZVrelTzXPO0WtMvWK96NVUrGaYh5UsOpCoJS1GBh/Tkm4LICoit0V0Gi3rrEzpEx6xrVAZ/3YdcIbbgFL+ovSqKj175Ea67tVvlHcEdyMCy+K1P4BU7xykCsFZ/94a9jFcCr/wxuFPfI9IYx+649/jFlZvGKQZM7sGkAIwJyPNatxIa9xIMtIcaxQ7stSddYPwCDrHUGmOOabhZ/H8UMXBI8QwfglStEhQmaKDz7NVXLeu8osRXTUeTREdzHzB/mXP00C5oNNc6r+Bwul/2OM3pwdfkiyIozofUDHMG/8rbuBEbsBqAdIBSlmEG3lckcnBJl0iE3YfMraoQh3OizMKEJIAIaKThwWExesR5ClQMNRUx1dSTg8+f8PvrRSGZHyIyOVB8eG6o9qAxUGFTRzIBuxK/rkftQHJpchd898rFSg4W50MXWuEpzi6fdCZ4C7Qda2BjrwNvboQFk9y4fGTTW6NybVW/cBMDFMnIuQDGoy7sl7BNCoz9mQz1tPqCLA7m7HSohosnnZL9un2AgHC3xFtD8J1Ps2dsXCHr8FKTWyf8aVxvnPTlOe1kjgRIWGZ7Be12BcEB+2qNEK0hpMTrJSBIgs24xuwfIQcksmLEDd7iF920dAkNeg8k7lxhZJ+UTK6GWvzc6QpyBYJDNC3PHs/9r3JS1htqnk89RXqHSjC4IlhkeKM6vCMSHqbHR2eNhEOQ3ozKnac4BZt6rLprj8rp45Et2jTmebUxDrVNPeIaGVHt9huR3k7o8rb5j0Exlprao5j0ptzaDcCMmyTdw51UUiGPOfQ5j0CJIFsT6c45vnf6ht86jc4H1KneIJCwcv7BPMmfS73hb9GqnCREqEoCMiDgMCrGxYCjKusQODVDzI2L6Ec/MGgIHDdBoYCH+VQW+tQx8KhlQ8KInIgInLaBsaI8bM6bUZHI75NhMMY8Q4fdsBGaQJBMzB8FXyrydL53x6vvb/BA/WKDj888skILM4275LHlgtug12Aq7W/C0LK5BTkz8QwTTwwtyD8BvCo5s4QpwP/S2Kkr9I8SBrfJIpJpDTXHBDtd/kzxEHv31Vj6ay+NnHbo/SLYiIm0Wv8jaX5u/8C9smPIcyLnN7+EZpK+NUz8NZS+W8FuYn76dy+qenWxvex39U1rlsPVbY8gdkoTJYhizxw7FFMgo8sgkMVraodQRbFmUeMCdk0IVu5eJwWbkHIMBaGro0TqFTwwkaVF9a6CfzE6KaqM4KTGHlLsFxOmp4LOnN3Z6KffnHgdI2H8j6Kg7PioWwWScLCoZpJlo7UZ3RnW/3m+sFT61t5mJjcaCunLdoK9T4OqFoUGWLQ723pRCsOvZ+voOXVhe5q/KrGYt1Z9SvoNpaQCRYZPYFl5kcb9AYlhhuPYyGAJ/vhfog+HZGfgRusD1NOGfcBTX4aZ02sq9cgW24XSK8qWuZUWqZtYu5ARJcIfPJYGX7+Jpc5IDE4ZdDFbp+RYhZ60YzQUcjoFxmioK1BkeEKOF2I9E/+MWJpBchBEAXZxaXIg+GXwvegwhuzoFc6K6jqVEPQV9JmQd2flztL9s+kFXC/zH4YDu3pqRLWrpi0tm4K8oyVP4JE62uh8ruFDVHTBJz0bBeS6nt5OUW9/eDUW2NdjyCTiqEJPljSmDf3rAQVbalUJv1RGJ3zR4ybqURvrjZvmhZd80cU2XKYQ+A1biQ5f4TLx/F8AAgF1TY5g1odbx2h2zWsdlzQGZo99czyh5n+bIp/1khk0BmDjjutTw0g63mqVX70Rlslg3I24zH4fc4L9AhCr6vm8z4a69ui0vS6JrI1pxOcTmWBmxrPoiyvVNbVa0vDUlCrZZ683JdJFcF6nq1jtHwZq865kMjwZDEZnsEJvxadVgShKQ7rRA6rbR7pQJTyoByWx/MyL2k+mrDg/w1gBOjwUsLvWfii+ORhhc+nTVmGwIcLoqpL/lql5pcif1FQ8rDy59eTlqn5Kwm3awl0HpkZoAKS5cJDGJA8KD50gzcK1Xxnyii89lgVGWjobCXKQwNvJVZnQhUWBsOCPbbZqBu83UiwEIHXxQsxGn9aOMgTO3PTRhQ7bzEUYt+navM2CR6iNraVqAt27CNCX8dQeYX9C9wc3+zjpzyIwJchFpqSeO8k0NimnceP8Mdq+JJifyf9bRlSn0hybNFGSzYvfIutkuxf+nxU67cdDRcBuxi+z4Gifs8Y9acHtorkLm/QCwJbA7xbT55B+ysE4MEvVglSDhAHtt6hWvfBS3toiMLGB7Eh3J9xUGzwFsDydcXSlUDU7UrofQtduAXjkEJnQcJVhYCZ3w4qoUIWo35bffOh4odl40W8ReOggOEJwTzYOWEzR48/MS0oT/ACKnhgwfPsH3spPpnNyRNJSmFIwo2ANh4YN4IJZ+wXQhBxG/78DvwXhZvvAjcC3nlg3PC0c9s+USqNmkyfVUBGD4wEnqnKg1Vw0CkXuMDZogoQPQNCQFYPDAievCoBsVjGK3DUeaF/KozIIrrGNlcLg7k5oeGvVossXuQ9qgTft+C9se1N3WwJYNqAbLFGCmGxVv5p73I39bHtRd3kmYliwGOSWwm9d6GbY5uGutkSk6SELk3oztjmn27ylEKp4XPaQEm9Z6kLsokMLfWWODQSobJYxxCZ/5gTUKLvV/SWPrpJp/G2/IFMMjgMpYaBWg4G4vgR91A3krf2TA7PrODx6cvt5/n88zy/6un1PsYro6rpNCqOZqW04m5SYrNyUpCUoyQ567k4Dh2xrA3PzeajsrwfDid4GKoTxaFEH25+Y6z3m5+isfTXYIkXK+PAEiVUYvtRSRgHvGvD9QGfl0Cc+N1zahs76dPjCVuLlKHezJlUl4NrmjE5mM0AHTQzvcpO/8oc0aMrx6l24WEwTP4C07WQLAzNQZ1q27pr1EBnzvSpYZfnjQa2umYzsBx7WnmGddpTJGc20DU+LKmZ5NOHyy26+ZvnLLABeoeaAiDcJ6hBd+hzmKyhvwMqPvE8TWfY9QBFk6W2qAwBU5QEu5gmkqDrWgKS83Voh1Iw0gFfS7KU15qjWior4+kWIh2gp8Q1CuEiz0LUW9jeaB+GF5f5pu3V3XLnBC95kjuW6uBCuyRFnxRCekeIILpxaIS00MJsGRNLMa1k37fsBUGOQ8u+hRrGslcrmqQIXRSpOLTUD6xXVcuaJIldEKc4tNgPsMM4J/MiW9d3TVcWoQQcCOIOh8bBj7ePzlVBpVT223YLnmakjXSYj9eBSWkBhdwdVJpEnenMqug5Xp+ucJeaa09nLPpoOz3W+T4MNX3sLZpYqIb8ndzPApajNRhgwzwIrGZ90zm81Y7D9gkX15cFxHH3mzpBL/aJtK46bBykNTfEMazZEEjgKbZ8LU9OtL2nxDm6yNYNMi3/9yv0kzvMumLW5Ko5M6XN39MnkO7DYlfOYheEZ1iZ1skvip7TPOG51q0oxKuTiSHPvHaNtF9b7iEGBy2+uJzSN7gsbtVvE/CqJ/bssjlTPN01DePlC2oBqjJFmLvovwl0Ui2G6Ck7P/I3AP/lr1YXZVP6f2oQreN/XFQnBCHNHInFjNkzARrYqXKiEHO11V29qOTQCwRIbvn5JJDQ/i8vF0llf9HKHduZv34nFpv5AK5Mjfeaig1danw7M10khFfwUfbF/iWPSCRkNvuyvs8Nm81V8y7nAcG06mrftvl5F+EWCIe2XfsgDFq2O9uofBDnuMnm6QkhRPKWGFXVstVZkBKyRMm8d5mLEj0MK3Omdb5/ZqRHD4AtXD/OYrTJb6D9gK2zgnzaNh9p32K3LYVlSRnUiahjW48U8p1qmleTseaZB6UsCg/qU/Jd95AwR93IxG6wBzOrYet1jfVxGjv4mkbjRmeH86BDGGP7vayOnMDtQ7wCuMb/AQ== \ No newline at end of file diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index c5614ba4..8c294537 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -111,7 +111,7 @@ dependencies = [ [[package]] name = "buddy_system_allocator" -version = "0.1.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -271,11 +271,6 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "once" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "os_bootinfo" version = "0.2.1" @@ -395,7 +390,7 @@ dependencies = [ "bitmap-allocator 0.1.0 (git+https://github.com/rcore-os/bitmap-allocator)", "bitvec 0.11.0 (git+https://github.com/myrrlyn/bitvec.git?rev=ed2aec38bfb5b1116e3585b1574c50655b9c85ec)", "bootloader 0.4.0 (git+https://github.com/rcore-os/bootloader)", - "buddy_system_allocator 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "buddy_system_allocator 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", "console-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "device_tree 1.0.3 (git+https://github.com/rcore-os/device_tree-rs)", @@ -404,7 +399,6 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mips 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "pc-keyboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "pci 0.0.1 (git+https://github.com/rcore-os/pci-rs)", @@ -699,7 +693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitvec 0.11.0 (git+https://github.com/myrrlyn/bitvec.git?rev=ed2aec38bfb5b1116e3585b1574c50655b9c85ec)" = "" "checksum bitvec 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfadef5c4e2c2e64067b9ecc061179f12ac7ec65ba613b1f60f3972bbada1f5b" "checksum bootloader 0.4.0 (git+https://github.com/rcore-os/bootloader)" = "" -"checksum buddy_system_allocator 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2ed828f1e227d6e32b998d6375b67fd63ac5389d50b23f258ce151d22b6cc595" +"checksum buddy_system_allocator 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "94a6c0143a07fea0db2f4b43cb9540dcc7c17af8a7beafdf2184e5e4e35aae91" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" "checksum cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a0c56216487bb80eec9c4516337b2588a4f2a2290d72a1416d930e4dcdb0c90d" @@ -723,7 +717,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" "checksum mips 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbf449a63e4db77af9f662d6b42068c0925e779a3a7c70ad02f191cf1e6c802" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "931fb7a4cf34610cf6cbe58d52a8ca5ef4c726d4e2e178abd0dc13a6551c6d73" "checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" "checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" "checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 969b4cbd..c6b32d9b 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -23,6 +23,8 @@ default = ["sv39"] # Page table sv39 or sv48 (for riscv64) sv39 = [] board_u540 = ["sv39", "link_user"] +board_k210 = ["sv39", "link_user"] +board_rocket_chip = ["sv39", "link_user"] # (for aarch64 RaspberryPi3) nographic = [] board_raspi3 = ["bcm2837", "link_user"] @@ -38,6 +40,8 @@ board_pc = ["link_user"] link_user = [] # Run cmdline instead of user shell, useful for automatic testing run_cmdline = [] +# Add performance profiling +profile = [] [profile.dev] # MUST >= 2 : Enable RVO to avoid stack overflow @@ -46,7 +50,6 @@ opt-level = 2 [dependencies] log = "0.4" spin = "0.5" -once = "0.3" xmas-elf = "0.6" bitflags = "1.0" bit_field = "0.9" @@ -54,7 +57,7 @@ volatile = "0.2" heapless = "0.4" bitvec = { git = "https://github.com/myrrlyn/bitvec.git", rev = "ed2aec38bfb5b1116e3585b1574c50655b9c85ec", default-features = false, features = ["alloc"] } console-traits = "0.3" -buddy_system_allocator = "0.1" +buddy_system_allocator = "0.3" pci = { git = "https://github.com/rcore-os/pci-rs" } device_tree = { git = "https://github.com/rcore-os/device_tree-rs" } isomorphic_drivers = { git = "https://github.com/rcore-os/isomorphic_drivers" } diff --git a/kernel/Makefile b/kernel/Makefile index af393e3e..d8bb97ac 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -23,13 +23,16 @@ # smp = 1 | 2 | ... SMP core number # graphic = on | off Enable/disable qemu graphical output # board = none Running on QEMU -# | pc Only available on x86_64, run on real pc +# | pc Only available on x86_64, run on real pc # | u540 Only available on riscv64, run on HiFive U540, use Sv39 +# | k210 Only available on riscv64, run on K210, use Sv39 +# | rocket_chip Only available on riscv64, run on Rocket Chip, use Sv39 # | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+ # pci_passthru = 0000:00:00.1 Only available on x86_64, passthrough the specified PCI device # init = /bin/ls Only available on riscv64, run specified program instead of user shell # extra_nic = on | off Only available on x86_64, add an additional e1000 nic # u_boot = /path/to/u-boot.bin Only available on aarch64, use u-boot to boot rcore +# . extra_features = profile | ... Add additional features arch ?= riscv64 board ?= none @@ -59,7 +62,7 @@ ifeq ($(arch), $(filter $(arch), aarch64 mipsel)) export SFSIMG = $(user_dir)/build/$(arch).img else # board is pc or qemu? -ifeq ($(board), pc) +ifeq ($(board), $(filter $(board), pc u540 k210 rocket_chip)) #link user img, so use original image export SFSIMG = $(user_dir)/build/$(arch).img else @@ -124,6 +127,7 @@ endif else ifeq ($(arch), riscv32) qemu_opts += \ -machine virt \ + -serial mon:stdio \ -kernel ../tools/opensbi/virt_rv32.elf \ -device loader,addr=0x80400000,file=$(kernel_img) \ -drive file=$(SFSIMG),format=qcow2,id=sfs \ @@ -136,11 +140,13 @@ else ifeq ($(arch), riscv64) ifeq ($(board), u540) qemu_opts += \ -machine virt \ + -serial mon:stdio \ -kernel ../tools/opensbi/fu540.elf \ -device loader,addr=0x80200000,file=$(kernel_img) else qemu_opts += \ -machine virt \ + -serial mon:stdio \ -kernel ../tools/opensbi/virt_rv64.elf \ -device loader,addr=0x80200000,file=$(kernel_img) \ -drive file=$(SFSIMG),format=qcow2,id=sfs \ @@ -201,15 +207,12 @@ features += raspi3_use_generic_timer endif endif -ifeq ($(board), u540) -features += sv39 -riscv_pk_args += --enable-sv39 -endif - ifneq ($(board), none) features += board_$(board) endif +features += $(extra_features) + build_args := --target targets/$(target).json --features "$(features)" ifeq ($(mode), release) @@ -270,7 +273,7 @@ justrunui: build -device virtio-mouse-device justruntest: build - @qemu-system-$(arch) $(qemu_opts) --append $(init) -serial file:../tests/stdout -monitor null + @qemu-system-$(arch) $(filter-out -serial mon:stdio, $(qemu_opts)) --append $(init) -serial file:../tests/stdout -monitor null debug: $(kernel) $(kernel_img) @qemu-system-$(arch) $(qemu_opts) -s -S & @@ -325,6 +328,11 @@ ifeq ($(arch), x86_64) @bootimage build $(build_args) @mv target/x86_64/bootimage.bin $(bootimage) else ifeq ($(arch), $(filter $(arch), riscv32 riscv64)) +ifeq ($(board), k210) + @cp src/arch/riscv32/board/k210/linker.ld src/arch/riscv32/boot/linker64.ld +else + @cp src/arch/riscv32/board/u540/linker.ld src/arch/riscv32/boot/linker64.ld +endif @-patch -p0 -N -b \ $(shell rustc --print sysroot)/lib/rustlib/src/rust/src/libcore/sync/atomic.rs \ src/arch/riscv32/atomic.patch @@ -367,10 +375,18 @@ ifeq ($(board), u540) .PHONY: install: $(kernel_img) @$(objcopy) -S -O binary ../tools/opensbi/fu540.elf $(build_path)/bin - @dd if=$< of=$(build_path)/bin bs=131072 seek=16 - @../tools/u540/mkimg.sh $(build_path)/bin $(build_path)/sd.img + @dd if=$< of=$(build_path)/bin bs=0x20000 seek=16 + @../tools/u540/mkimg.sh $(build_path)/bin $(build_path)/u540.img +endif + +ifeq ($(board), k210) +.PHONY: +install: $(kernel_img) + @$(objcopy) -S -O binary ../tools/opensbi/k210.elf $(build_path)/k210.img + @dd if=$< of=$(build_path)/k210.img bs=0x10000 seek=1 + @python3 ../tools/k210/kflash.py -b 600000 $(build_path)/k210.img endif .PHONY: addr2line: - @python3 ../tools/addr2line.py $(prefix)addr2line $(arch) $(mode) + @python3.7 ../tools/addr2line.py $(prefix)addr2line $(arch) $(mode) diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 1cc66967..0480e350 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -2,7 +2,6 @@ use alloc::string::String; use bcm2837::atags::Atags; -use once::*; #[path = "../../../../drivers/gpu/fb.rs"] pub mod fb; @@ -18,10 +17,7 @@ pub const IO_REMAP_END: usize = bcm2837::consts::KERNEL_OFFSET + 0x4000_1000; /// Initialize serial port before other initializations. pub fn init_serial_early() { - assert_has_not_been_called!("board::init must be called only once"); - serial::init(); - println!("Hello Raspberry Pi!"); } diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs index b7796d32..b2a1577e 100644 --- a/kernel/src/arch/aarch64/board/raspi3/serial.rs +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -1,7 +1,6 @@ use bcm2837::mini_uart::{MiniUart, MiniUartInterruptId}; use core::fmt; use lazy_static::lazy_static; -use once::*; use spin::Mutex; /// Struct to get a global SerialPort interface @@ -23,8 +22,6 @@ impl SerialPort { /// Init a newly created SerialPort, can only be called once. fn init(&mut self) { - assert_has_not_been_called!("SerialPort::init must be called only once"); - self.mu.init(); super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq); } diff --git a/kernel/src/arch/aarch64/driver/mod.rs b/kernel/src/arch/aarch64/driver/mod.rs index a100c2b5..d80ab0c2 100644 --- a/kernel/src/arch/aarch64/driver/mod.rs +++ b/kernel/src/arch/aarch64/driver/mod.rs @@ -1,7 +1,6 @@ //! ARM64 drivers use super::board; -use once::*; pub use self::board::fb; pub use self::board::serial; @@ -10,8 +9,6 @@ pub mod console; /// Initialize ARM64 common drivers pub fn init() { - assert_has_not_been_called!("driver::init must be called only once"); - board::init_driver(); console::init(); } diff --git a/kernel/src/arch/aarch64/interrupt/context.rs b/kernel/src/arch/aarch64/interrupt/context.rs index 87f3a4eb..03b44147 100644 --- a/kernel/src/arch/aarch64/interrupt/context.rs +++ b/kernel/src/arch/aarch64/interrupt/context.rs @@ -32,7 +32,7 @@ impl TrapFrame { tf.spsr = 0b1101_00_0101; // To EL 1, enable IRQ tf } - fn new_user_thread(entry_addr: usize, sp: usize) -> Self { + pub fn new_user_thread(entry_addr: usize, sp: usize) -> Self { use core::mem::zeroed; let mut tf: Self = unsafe { zeroed() }; tf.sp = sp; @@ -40,9 +40,6 @@ impl TrapFrame { tf.spsr = 0b1101_00_0000; // To EL 0, enable IRQ tf } - pub fn is_user(&self) -> bool { - unimplemented!() - } } /// 新线程的内核栈初始内容 @@ -201,11 +198,6 @@ impl Context { } .push_at(kstack_top, ttbr) } - /// Called at a new user context - /// To get the init TrapFrame in sys_exec - pub unsafe fn get_init_tf(&self) -> TrapFrame { - (*(self.stack_top as *const InitStack)).tf.clone() - } } const ASID_MASK: u16 = 0xffff; diff --git a/kernel/src/arch/mipsel/board/malta/mod.rs b/kernel/src/arch/mipsel/board/malta/mod.rs index 0fbaa338..7b54808e 100644 --- a/kernel/src/arch/mipsel/board/malta/mod.rs +++ b/kernel/src/arch/mipsel/board/malta/mod.rs @@ -1,7 +1,6 @@ use crate::drivers::bus::pci; use alloc::string::String; use mips::registers::cp0; -use once::*; #[path = "../../../../drivers/console/mod.rs"] pub mod console; @@ -17,7 +16,6 @@ use fb::FramebufferInfo; /// Initialize serial port first pub fn init_serial_early() { - assert_has_not_been_called!("board::init must be called only once"); // initialize serial driver serial::init(0xbf000900); // Enable serial interrupt diff --git a/kernel/src/arch/mipsel/board/mipssim/mod.rs b/kernel/src/arch/mipsel/board/mipssim/mod.rs index 97eb4656..be687086 100644 --- a/kernel/src/arch/mipsel/board/mipssim/mod.rs +++ b/kernel/src/arch/mipsel/board/mipssim/mod.rs @@ -1,5 +1,4 @@ use alloc::string::String; -use once::*; #[path = "../../../../drivers/console/mod.rs"] pub mod console; @@ -11,7 +10,6 @@ pub mod serial; /// Initialize serial port first pub fn init_serial_early() { - assert_has_not_been_called!("board::init must be called only once"); serial::init(0xbfd003f8); println!("Hello QEMU MIPSSIM!"); } diff --git a/kernel/src/arch/mipsel/board/thinpad/mod.rs b/kernel/src/arch/mipsel/board/thinpad/mod.rs index dae1f929..2afb19f1 100644 --- a/kernel/src/arch/mipsel/board/thinpad/mod.rs +++ b/kernel/src/arch/mipsel/board/thinpad/mod.rs @@ -1,5 +1,4 @@ use alloc::string::String; -use once::*; #[path = "../../../../drivers/console/mod.rs"] pub mod console; @@ -14,7 +13,6 @@ use fb::FramebufferResult; /// Initialize serial port first pub fn init_serial_early() { - assert_has_not_been_called!("board::init must be called only once"); serial::init(0xa3000000); println!("Hello ThinPad!"); } diff --git a/kernel/src/arch/mipsel/context.rs b/kernel/src/arch/mipsel/context.rs index ab4e5aa0..606fbad9 100644 --- a/kernel/src/arch/mipsel/context.rs +++ b/kernel/src/arch/mipsel/context.rs @@ -76,7 +76,7 @@ impl TrapFrame { /// /// The new thread starts at `entry_addr`. /// The stack pointer will be set to `sp`. - fn new_user_thread(entry_addr: usize, sp: usize) -> Self { + pub fn new_user_thread(entry_addr: usize, sp: usize) -> Self { use core::mem::zeroed; let mut tf: Self = unsafe { zeroed() }; tf.sp = sp; @@ -269,9 +269,4 @@ impl Context { } .push_at(kstack_top) } - - /// Used for getting the init TrapFrame of a new user context in `sys_exec`. - pub unsafe fn get_init_tf(&self) -> TrapFrame { - (*(self.sp as *const InitStack)).tf.clone() - } } diff --git a/kernel/src/arch/mipsel/driver/mod.rs b/kernel/src/arch/mipsel/driver/mod.rs index 0cf6ad0c..7fb6fd0f 100644 --- a/kernel/src/arch/mipsel/driver/mod.rs +++ b/kernel/src/arch/mipsel/driver/mod.rs @@ -1,7 +1,6 @@ //! mipsel drivers use super::board; -use once::*; pub use self::board::fb; pub use self::board::serial; @@ -10,7 +9,6 @@ pub mod console; /// Initialize common drivers pub fn init() { - assert_has_not_been_called!("driver::init must be called only once"); board::init_driver(); console::init(); } diff --git a/kernel/src/arch/riscv32/board/k210/linker.ld b/kernel/src/arch/riscv32/board/k210/linker.ld new file mode 100644 index 00000000..7abc05b7 --- /dev/null +++ b/kernel/src/arch/riscv32/board/k210/linker.ld @@ -0,0 +1,49 @@ +/* Copy from bbl-ucore : https://ring00.github.io/bbl-ucore */ + +/* Simple linker script for the ucore kernel. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +BASE_ADDRESS = 0xffffffffc0010000; + +SECTIONS +{ + /* Load the kernel at this address: "." means the current address */ + . = BASE_ADDRESS; + start = .; + + .text : { + stext = .; + *(.text.entry) + *(.text .text.*) + . = ALIGN(4K); + etext = .; + } + + .rodata : { + srodata = .; + *(.rodata .rodata.*) + . = ALIGN(4K); + erodata = .; + } + + .data : { + sdata = .; + *(.data .data.*) + edata = .; + } + + .stack : { + *(.bss.stack) + } + + .bss : { + sbss = .; + *(.bss .bss.*) + ebss = .; + } + + PROVIDE(end = .); +} diff --git a/kernel/src/arch/riscv32/board/u540/linker.ld b/kernel/src/arch/riscv32/board/u540/linker.ld new file mode 100644 index 00000000..87ca826c --- /dev/null +++ b/kernel/src/arch/riscv32/board/u540/linker.ld @@ -0,0 +1,49 @@ +/* Copy from bbl-ucore : https://ring00.github.io/bbl-ucore */ + +/* Simple linker script for the ucore kernel. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +BASE_ADDRESS = 0xffffffffc0200000; + +SECTIONS +{ + /* Load the kernel at this address: "." means the current address */ + . = BASE_ADDRESS; + start = .; + + .text : { + stext = .; + *(.text.entry) + *(.text .text.*) + . = ALIGN(4K); + etext = .; + } + + .rodata : { + srodata = .; + *(.rodata .rodata.*) + . = ALIGN(4K); + erodata = .; + } + + .data : { + sdata = .; + *(.data .data.*) + edata = .; + } + + .stack : { + *(.bss.stack) + } + + .bss : { + sbss = .; + *(.bss .bss.*) + ebss = .; + } + + PROVIDE(end = .); +} diff --git a/kernel/src/arch/riscv32/boot/entry32.asm b/kernel/src/arch/riscv32/boot/entry32.asm index 22cc0eed..3ea0676c 100644 --- a/kernel/src/arch/riscv32/boot/entry32.asm +++ b/kernel/src/arch/riscv32/boot/entry32.asm @@ -8,7 +8,7 @@ _start: # 1. set sp # sp = bootstack + (hartid + 1) * 0x10000 add t0, a0, 1 - slli t0, t0, 16 + slli t0, t0, 14 lui sp, %hi(bootstack) add sp, sp, t0 @@ -32,7 +32,7 @@ _start: .align 12 # page align .global bootstack bootstack: - .space 4096 * 16 * 8 + .space 4096 * 4 * 8 .global bootstacktop bootstacktop: diff --git a/kernel/src/arch/riscv32/boot/entry64.asm b/kernel/src/arch/riscv32/boot/entry64.asm index 69727d5a..6fbedbb7 100644 --- a/kernel/src/arch/riscv32/boot/entry64.asm +++ b/kernel/src/arch/riscv32/boot/entry64.asm @@ -8,7 +8,7 @@ _start: # 1. set sp # sp = bootstack + (hartid + 1) * 0x10000 add t0, a0, 1 - slli t0, t0, 16 + slli t0, t0, 14 lui sp, %hi(bootstack) add sp, sp, t0 @@ -32,7 +32,7 @@ _start: .align 12 # page align .global bootstack bootstack: - .space 4096 * 16 * 8 + .space 4096 * 4 * 8 .global bootstacktop bootstacktop: diff --git a/kernel/src/arch/riscv32/boot/entry_k210.asm b/kernel/src/arch/riscv32/boot/entry_k210.asm new file mode 100644 index 00000000..29a65e07 --- /dev/null +++ b/kernel/src/arch/riscv32/boot/entry_k210.asm @@ -0,0 +1,30 @@ + .section .text.entry + .globl _start +_start: + # a0 == hartid + # pc == 0x80010000 + # sp == 0x8000xxxx + + # 1. set sp + # sp = bootstack + (hartid + 1) * 0x10000 + add t0, a0, 1 + slli t0, t0, 14 + lui sp, %hi(bootstack) + add sp, sp, t0 + + # 1.1 set device tree paddr + # OpenSBI give me 0 ??? + li a1, 0x800003b0 + + # 2. jump to rust_main (absolute address) + lui t0, %hi(rust_main) + addi t0, t0, %lo(rust_main) + jr t0 + + .section .bss.stack + .align 12 # page align + .global bootstack +bootstack: + .space 4096 * 4 * 2 + .global bootstacktop +bootstacktop: diff --git a/kernel/src/arch/riscv32/consts.rs b/kernel/src/arch/riscv32/consts.rs index 30420545..01b9f46f 100644 --- a/kernel/src/arch/riscv32/consts.rs +++ b/kernel/src/arch/riscv32/consts.rs @@ -22,10 +22,16 @@ pub const KERNEL_P2_INDEX: usize = (KERNEL_OFFSET >> 12 >> 10) & 0x3ff; #[cfg(target_arch = "riscv64")] pub const KERNEL_P4_INDEX: usize = (KERNEL_OFFSET >> 12 >> 9 >> 9 >> 9) & 0o777; +#[cfg(feature = "board_k210")] +pub const KERNEL_HEAP_SIZE: usize = 0x0020_0000; +#[cfg(not(feature = "board_k210"))] pub const KERNEL_HEAP_SIZE: usize = 0x0080_0000; pub const MEMORY_OFFSET: usize = 0x8000_0000; // TODO: get memory end from device tree +#[cfg(feature = "board_k210")] +pub const MEMORY_END: usize = 0x8060_0000; +#[cfg(not(feature = "board_k210"))] pub const MEMORY_END: usize = 0x8800_0000; // FIXME: rv64 `sh` and `ls` will crash if stack top > 0x80000000 ??? diff --git a/kernel/src/arch/riscv32/context.rs b/kernel/src/arch/riscv32/context.rs index 51df0c1b..c8ac7eae 100644 --- a/kernel/src/arch/riscv32/context.rs +++ b/kernel/src/arch/riscv32/context.rs @@ -40,7 +40,7 @@ impl TrapFrame { /// /// The new thread starts at `entry_addr`. /// The stack pointer will be set to `sp`. - fn new_user_thread(entry_addr: usize, sp: usize) -> Self { + pub fn new_user_thread(entry_addr: usize, sp: usize) -> Self { use core::mem::zeroed; let mut tf: Self = unsafe { zeroed() }; tf.x[2] = sp; @@ -289,9 +289,4 @@ impl Context { } .push_at(kstack_top) } - - /// Used for getting the init TrapFrame of a new user context in `sys_exec`. - pub unsafe fn get_init_tf(&self) -> TrapFrame { - (*(self.sp as *const InitStack)).tf.clone() - } } diff --git a/kernel/src/arch/riscv32/memory.rs b/kernel/src/arch/riscv32/memory.rs index af7333b2..314c7f22 100644 --- a/kernel/src/arch/riscv32/memory.rs +++ b/kernel/src/arch/riscv32/memory.rs @@ -9,6 +9,8 @@ use riscv::{addr::*, register::sstatus}; /// Initialize the memory management module pub fn init(dtb: usize) { // allow user memory access + // NOTE: In K210 priv v1.9.1, sstatus.SUM is PUM which has opposite meaning! + #[cfg(not(feature = "board_k210"))] unsafe { sstatus::set_sum(); } @@ -90,6 +92,8 @@ fn remap_the_kernel(dtb: usize) { Linear::new(offset), "bss", ); + // TODO: dtb on rocket chip + #[cfg(not(feature = "board_rocket_chip"))] ms.push( dtb, dtb + super::consts::MAX_DTB_SIZE, diff --git a/kernel/src/arch/riscv32/mod.rs b/kernel/src/arch/riscv32/mod.rs index 1b30ad30..75b7cccc 100644 --- a/kernel/src/arch/riscv32/mod.rs +++ b/kernel/src/arch/riscv32/mod.rs @@ -53,9 +53,11 @@ pub extern "C" fn rust_main(hartid: usize, device_tree_paddr: usize) -> ! { memory::init(device_tree_vaddr); timer::init(); // FIXME: init driver on u540 - #[cfg(not(feature = "board_u540"))] + #[cfg(not(any(feature = "board_u540", feature = "board_rocket_chip")))] crate::drivers::init(device_tree_vaddr); + #[cfg(not(feature = "board_k210"))] unsafe { + #[cfg(not(feature = "board_rocket_chip"))] board::enable_serial_interrupt(); board::init_external_interrupt(); } @@ -108,6 +110,8 @@ global_asm!( ); #[cfg(target_arch = "riscv32")] global_asm!(include_str!("boot/entry32.asm")); -#[cfg(target_arch = "riscv64")] +#[cfg(all(target_arch = "riscv64", not(feature = "board_k210")))] global_asm!(include_str!("boot/entry64.asm")); +#[cfg(feature = "board_k210")] +global_asm!(include_str!("boot/entry_k210.asm")); global_asm!(include_str!("boot/trap.asm")); diff --git a/kernel/src/arch/x86_64/consts.rs b/kernel/src/arch/x86_64/consts.rs index 0b3a4f97..d8725021 100644 --- a/kernel/src/arch/x86_64/consts.rs +++ b/kernel/src/arch/x86_64/consts.rs @@ -1,96 +1,6 @@ -// Copy from Redox consts.rs: - -// Because the memory map is so important to not be aliased, it is defined here, in one place -// The lower 256 PML4 entries are reserved for userspace -// Each PML4 entry references up to 512 GB of memory -// The top (511) PML4 is reserved for recursive mapping -// The second from the top (510) PML4 is reserved for the kernel -/// The size of a single PML4 -pub const PML4_SIZE: usize = 0x0000_0080_0000_0000; -pub const PML4_MASK: usize = 0x0000_ff80_0000_0000; - -/// Offset of recursive paging -pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize; -pub const RECURSIVE_PAGE_PML4: usize = (RECURSIVE_PAGE_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset of kernel -pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE; -pub const KERNEL_PML4: usize = (KERNEL_OFFSET & PML4_MASK) / PML4_SIZE; - -pub const KERNEL_SIZE: usize = PML4_SIZE; - -/// Offset to kernel heap -pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE; -pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK) / PML4_SIZE; -/// Size of kernel heap -pub const KERNEL_HEAP_SIZE: usize = 32 * 1024 * 1024; // 32 MB - pub const MEMORY_OFFSET: usize = 0; +pub const KERNEL_OFFSET: usize = 0xffffff00_00000000; +pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024; // 8 MB -/// Offset to kernel percpu variables -//TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE; -pub const KERNEL_PERCPU_OFFSET: usize = 0xC000_0000; -/// Size of kernel percpu variables -pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB - -/// Offset to user image -pub const USER_OFFSET: usize = 0; -pub const USER_PML4: usize = (USER_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user TCB -pub const USER_TCB_OFFSET: usize = 0xB000_0000; - -/// Offset to user arguments -pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE / 2; - -/// Offset to user heap -pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE; -pub const USER_HEAP_PML4: usize = (USER_HEAP_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user grants -pub const USER_GRANT_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE; -pub const USER_GRANT_PML4: usize = (USER_GRANT_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user stack -pub const USER_STACK_OFFSET: usize = USER_GRANT_OFFSET + PML4_SIZE; -pub const USER_STACK_PML4: usize = (USER_STACK_OFFSET & PML4_MASK) / PML4_SIZE; -/// Size of user stack +pub const USER_STACK_OFFSET: usize = 0x00008000_00000000 - USER_STACK_SIZE; pub const USER_STACK_SIZE: usize = 8 * 1024 * 1024; // 8 MB, the default config of Linux - -/// Offset to user sigstack -pub const USER_SIGSTACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE; -pub const USER_SIGSTACK_PML4: usize = (USER_SIGSTACK_OFFSET & PML4_MASK) / PML4_SIZE; -/// Size of user sigstack -pub const USER_SIGSTACK_SIZE: usize = 256 * 1024; // 256 KB - -/// Offset to user TLS -pub const USER_TLS_OFFSET: usize = USER_SIGSTACK_OFFSET + PML4_SIZE; -pub const USER_TLS_PML4: usize = (USER_TLS_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user temporary image (used when cloning) -pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE; -pub const USER_TMP_PML4: usize = (USER_TMP_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user temporary heap (used when cloning) -pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE; -pub const USER_TMP_HEAP_PML4: usize = (USER_TMP_HEAP_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user temporary page for grants -pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE; -pub const USER_TMP_GRANT_PML4: usize = (USER_TMP_GRANT_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user temporary stack (used when cloning) -pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE; -pub const USER_TMP_STACK_PML4: usize = (USER_TMP_STACK_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user temporary sigstack (used when cloning) -pub const USER_TMP_SIGSTACK_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE; -pub const USER_TMP_SIGSTACK_PML4: usize = (USER_TMP_SIGSTACK_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset to user temporary tls (used when cloning) -pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_SIGSTACK_OFFSET + PML4_SIZE; -pub const USER_TMP_TLS_PML4: usize = (USER_TMP_TLS_OFFSET & PML4_MASK) / PML4_SIZE; - -/// Offset for usage in other temporary pages -pub const USER_TMP_MISC_OFFSET: usize = USER_TMP_TLS_OFFSET + PML4_SIZE; -pub const USER_TMP_MISC_PML4: usize = (USER_TMP_MISC_OFFSET & PML4_MASK) / PML4_SIZE; diff --git a/kernel/src/arch/x86_64/driver/mod.rs b/kernel/src/arch/x86_64/driver/mod.rs index 02158ed0..cbc4d673 100644 --- a/kernel/src/arch/x86_64/driver/mod.rs +++ b/kernel/src/arch/x86_64/driver/mod.rs @@ -1,5 +1,3 @@ -use once::*; - pub mod ide; pub mod keyboard; pub mod pic; @@ -9,8 +7,6 @@ pub mod serial; pub mod vga; pub fn init() { - assert_has_not_been_called!(); - // Use IOAPIC instead of PIC pic::disable(); diff --git a/kernel/src/arch/x86_64/driver/pic.rs b/kernel/src/arch/x86_64/driver/pic.rs index 7a67a3cb..d35ca5c4 100644 --- a/kernel/src/arch/x86_64/driver/pic.rs +++ b/kernel/src/arch/x86_64/driver/pic.rs @@ -1,7 +1,6 @@ // Copy from Redox use log::*; -use once::*; use spin::Mutex; use x86_64::instructions::port::Port; @@ -18,8 +17,6 @@ pub fn disable() { } pub unsafe fn init() { - assert_has_not_been_called!("pic::init must be called only once"); - let mut master = MASTER.lock(); let mut slave = SLAVE.lock(); diff --git a/kernel/src/arch/x86_64/driver/pit.rs b/kernel/src/arch/x86_64/driver/pit.rs index 655ce8af..2b88280d 100644 --- a/kernel/src/arch/x86_64/driver/pit.rs +++ b/kernel/src/arch/x86_64/driver/pit.rs @@ -1,9 +1,7 @@ use log::*; -use once::*; use x86_64::instructions::port::Port; pub fn init() { - assert_has_not_been_called!("pit::init must be called only once"); Pit::new(0x40).init(100); info!("pit: init end"); } diff --git a/kernel/src/arch/x86_64/driver/serial.rs b/kernel/src/arch/x86_64/driver/serial.rs index 68655e2c..8242fdbf 100644 --- a/kernel/src/arch/x86_64/driver/serial.rs +++ b/kernel/src/arch/x86_64/driver/serial.rs @@ -1,4 +1,3 @@ -use once::*; use spin::Mutex; use uart_16550::SerialPort; use x86_64::instructions::port::Port; @@ -9,8 +8,6 @@ pub static COM1: Mutex = Mutex::new(unsafe { SerialPort::new(0x3F8) pub static COM2: Mutex = Mutex::new(unsafe { SerialPort::new(0x2F8) }); pub fn init() { - assert_has_not_been_called!("serial::init must be called only once"); - COM1.lock().init(); COM2.lock().init(); enable_irq(consts::COM1); diff --git a/kernel/src/arch/x86_64/gdt.rs b/kernel/src/arch/x86_64/gdt.rs index ed648e34..42c125ce 100644 --- a/kernel/src/arch/x86_64/gdt.rs +++ b/kernel/src/arch/x86_64/gdt.rs @@ -39,10 +39,6 @@ pub struct Cpu { } impl Cpu { - pub fn current() -> &'static mut Self { - unsafe { CPUS[super::cpu::id()].as_mut().unwrap() } - } - fn new() -> Self { Cpu { gdt: GlobalDescriptorTable::new(), @@ -72,18 +68,9 @@ impl Cpu { set_cs(KCODE_SELECTOR); // load TSS load_tss(TSS_SELECTOR); - // for fast syscall: - // store address of TSS to kernel_gsbase - let mut kernel_gsbase = Msr::new(0xC0000102); - kernel_gsbase.write(&self.tss as *const _ as u64); - } - - /// 设置从Ring3跳到Ring0时,自动切换栈的地址 - /// - /// 每次进入用户态前,都要调用此函数,才能保证正确返回内核态 - pub fn set_ring0_rsp(&mut self, rsp: usize) { - trace!("gdt.set_ring0_rsp: {:#x}", rsp); - self.tss.privilege_stack_table[0] = VirtAddr::new(rsp as u64); + // store address of TSS to GSBase + let mut gsbase = Msr::new(0xC0000101); + gsbase.write(&self.tss as *const _ as u64); } } diff --git a/kernel/src/arch/x86_64/interrupt/fast_syscall.rs b/kernel/src/arch/x86_64/interrupt/fast_syscall.rs index 69170ac0..d096b65d 100644 --- a/kernel/src/arch/x86_64/interrupt/fast_syscall.rs +++ b/kernel/src/arch/x86_64/interrupt/fast_syscall.rs @@ -10,9 +10,9 @@ pub fn init() { *flags |= EferFlags::SYSTEM_CALL_EXTENSIONS; }); - let mut star = Msr::new(0xC0000081); - let mut lstar = Msr::new(0xC0000082); - let mut sfmask = Msr::new(0xC0000084); + let mut star = Msr::new(0xC0000081); // legacy mode SYSCALL target + let mut lstar = Msr::new(0xC0000082); // long mode SYSCALL target + let mut sfmask = Msr::new(0xC0000084); // EFLAGS mask for syscall // flags to clear on syscall // copy from Linux 5.0 diff --git a/kernel/src/arch/x86_64/interrupt/handler.rs b/kernel/src/arch/x86_64/interrupt/handler.rs index 230ee187..de708644 100644 --- a/kernel/src/arch/x86_64/interrupt/handler.rs +++ b/kernel/src/arch/x86_64/interrupt/handler.rs @@ -213,9 +213,3 @@ fn invalid_opcode(tf: &mut TrapFrame) { fn error(tf: &TrapFrame) { crate::trap::error(tf); } - -#[no_mangle] -pub unsafe extern "C" fn set_return_rsp(tf: *const TrapFrame) { - use crate::arch::gdt::Cpu; - Cpu::current().set_ring0_rsp(tf.add(1) as usize); -} diff --git a/kernel/src/arch/x86_64/interrupt/trap.asm b/kernel/src/arch/x86_64/interrupt/trap.asm index d20528d5..2f0424b5 100644 --- a/kernel/src/arch/x86_64/interrupt/trap.asm +++ b/kernel/src/arch/x86_64/interrupt/trap.asm @@ -49,8 +49,10 @@ __alltraps: .global trap_ret trap_ret: + # store kernel rsp -> TSS.sp0 mov rdi, rsp - call set_return_rsp + add rdi, 720 + mov gs:[4], rdi # pop fp state offset pop rcx @@ -104,8 +106,6 @@ syscall_entry: # - store rip -> rcx # - load rip - # swap in kernel gs - swapgs # store user rsp -> scratch at TSS.sp1 mov gs:[12], rsp # load kernel rsp <- TSS.sp0 @@ -119,11 +119,8 @@ syscall_entry: push 0 # error_code (dummy) push 0 # trap_num (dummy) - # swap out kernel gs - swapgs - # enable interrupt - # sti + sti push rax push rcx @@ -173,8 +170,10 @@ syscall_return: # disable interrupt cli + # store kernel rsp -> TSS.sp0 mov rdi, rsp - call set_return_rsp + add rdi, 720 + mov gs:[4], rdi # pop fp state offset pop rcx diff --git a/kernel/src/arch/x86_64/interrupt/trapframe.rs b/kernel/src/arch/x86_64/interrupt/trapframe.rs index d9ab7f7c..fe01ee27 100644 --- a/kernel/src/arch/x86_64/interrupt/trapframe.rs +++ b/kernel/src/arch/x86_64/interrupt/trapframe.rs @@ -73,7 +73,7 @@ impl TrapFrame { tf.fpstate_offset = 16; // skip restoring for first time tf } - fn new_user_thread(entry_addr: usize, rsp: usize) -> Self { + pub fn new_user_thread(entry_addr: usize, rsp: usize) -> Self { use crate::arch::gdt; let mut tf = TrapFrame::default(); tf.cs = gdt::UCODE_SELECTOR.0 as usize; @@ -234,9 +234,4 @@ impl Context { } .push_at(kstack_top) } - /// Called at a new user context - /// To get the init TrapFrame in sys_exec - pub unsafe fn get_init_tf(&self) -> TrapFrame { - (*(self.0 as *const InitStack)).tf.clone() - } } diff --git a/kernel/src/arch/x86_64/memory.rs b/kernel/src/arch/x86_64/memory.rs index cadd7381..c5afb85f 100644 --- a/kernel/src/arch/x86_64/memory.rs +++ b/kernel/src/arch/x86_64/memory.rs @@ -2,20 +2,15 @@ use crate::consts::KERNEL_OFFSET; use bitmap_allocator::BitAlloc; // Depends on kernel use super::{BootInfo, MemoryRegionType}; -use crate::memory::{active_table, alloc_frame, init_heap, FRAME_ALLOCATOR}; -use crate::HEAP_ALLOCATOR; -use alloc::vec::Vec; +use crate::memory::{active_table, init_heap, FRAME_ALLOCATOR}; use log::*; -use once::*; use rcore_memory::paging::*; use rcore_memory::PAGE_SIZE; pub fn init(boot_info: &BootInfo) { - assert_has_not_been_called!("memory::init must be called only once"); init_frame_allocator(boot_info); init_device_vm_map(); init_heap(); - enlarge_heap(); info!("memory: init end"); } @@ -42,30 +37,3 @@ fn init_device_vm_map() { .map(KERNEL_OFFSET + 0xfee00000, 0xfee00000) .update(); } - -fn enlarge_heap() { - let mut page_table = active_table(); - let mut addrs = Vec::new(); - let va_offset = KERNEL_OFFSET + 0xe0000000; - for i in 0..16384 { - let page = alloc_frame().unwrap(); - let va = KERNEL_OFFSET + 0xe0000000 + page; - if let Some((ref mut addr, ref mut len)) = addrs.last_mut() { - if *addr - PAGE_SIZE == va { - *len += PAGE_SIZE; - *addr -= PAGE_SIZE; - continue; - } - } - addrs.push((va, PAGE_SIZE)); - } - for (addr, len) in addrs.into_iter() { - for va in (addr..(addr + len)).step_by(PAGE_SIZE) { - page_table.map(va, va - va_offset).update(); - } - info!("Adding {:#X} {:#X} to heap", addr, len); - unsafe { - HEAP_ALLOCATOR.lock().init(addr, len); - } - } -} diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 4b5f56cd..f02dd602 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -15,7 +15,7 @@ pub mod rand; pub mod syscall; pub mod timer; -static AP_CAN_INIT: AtomicBool = ATOMIC_BOOL_INIT; +static AP_CAN_INIT: AtomicBool = AtomicBool::new(false); /// The entry point of kernel #[no_mangle] // don't mangle the name of this function @@ -40,26 +40,33 @@ pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! { memory::init(boot_info); // Now heap is available + + // Init GDT gdt::init(); - + //get local apic id of cpu cpu::init(); - + // Use IOAPIC instead of PIC, use APIC Timer instead of PIT, init serial&keyboard in x86_64 driver::init(); - + // init pci/bus-based devices ,e.g. Intel 10Gb NIC, ... crate::drivers::init(); - + // init cpu scheduler and process manager, and add user shell app in process manager crate::process::init(); - + //wake up other CPUs AP_CAN_INIT.store(true, Ordering::Relaxed); - + //call the first main function in kernel. crate::kmain(); } /// The entry point for other processors fn other_start() -> ! { + // Init trap handling. idt::init(); + // init gdt gdt::init(); + // init local apic cpu::init(); + // setup fast syscall in xv6-64 interrupt::fast_syscall::init(); + //call the first main function in kernel. crate::kmain(); } diff --git a/kernel/src/arch/x86_64/paging.rs b/kernel/src/arch/x86_64/paging.rs index fb45522b..b2804465 100644 --- a/kernel/src/arch/x86_64/paging.rs +++ b/kernel/src/arch/x86_64/paging.rs @@ -47,23 +47,21 @@ impl PageTable for ActivePageTable { fn map(&mut self, addr: usize, target: usize) -> &mut Entry { let flags = EF::PRESENT | EF::WRITABLE | EF::NO_EXECUTE; unsafe { - if let Ok(flush) = self.0.map_to( - Page::of_addr(addr), - Frame::of_addr(target), - flags, - &mut FrameAllocatorForX86, - ) { - flush.flush(); - } + self.0 + .map_to( + Page::of_addr(addr), + Frame::of_addr(target), + flags, + &mut FrameAllocatorForX86, + ) + .unwrap() + .flush(); } unsafe { &mut *(get_entry_ptr(addr, 1)) } } fn unmap(&mut self, addr: usize) { - // unmap and flush if it is mapped - if let Ok((_, flush)) = self.0.unmap(Page::of_addr(addr)) { - flush.flush(); - } + self.0.unmap(Page::of_addr(addr)).unwrap().1.flush(); } fn get_entry(&mut self, addr: usize) -> Option<&mut Entry> { @@ -238,6 +236,9 @@ impl InactivePageTable for InactivePageTable0 { fn edit(&mut self, f: impl FnOnce(&mut Self::Active) -> T) -> T { let target = Cr3::read().0.start_address().as_u64() as usize; + if self.p4_frame == Cr3::read().0 { + return f(&mut active_table()); + } active_table().with_temporary_map(target, |active_table, p4_table: &mut x86PageTable| { let backup = p4_table[0o777].clone(); diff --git a/kernel/src/drivers/block/ahci.rs b/kernel/src/drivers/block/ahci.rs index 189c6e4f..f86b9094 100644 --- a/kernel/src/drivers/block/ahci.rs +++ b/kernel/src/drivers/block/ahci.rs @@ -48,8 +48,6 @@ pub fn init(_irq: Option, header: usize, size: usize) -> Arc { let ahci = AHCI::new(header, size); let driver = Arc::new(AHCIDriver(Mutex::new(ahci))); DRIVERS.write().push(driver.clone()); - BLK_DRIVERS - .write() - .push(Arc::new(BlockDriver(driver.clone()))); + BLK_DRIVERS.write().push(driver.clone()); driver } diff --git a/kernel/src/drivers/block/virtio_blk.rs b/kernel/src/drivers/block/virtio_blk.rs index c992aafd..3a561f4d 100644 --- a/kernel/src/drivers/block/virtio_blk.rs +++ b/kernel/src/drivers/block/virtio_blk.rs @@ -12,8 +12,6 @@ use rcore_memory::paging::PageTable; use rcore_memory::PAGE_SIZE; use volatile::Volatile; -use rcore_fs::dev::BlockDevice; - use crate::drivers::BlockDriver; use crate::memory::active_table; use crate::sync::SpinNoIrqLock as Mutex; @@ -224,5 +222,5 @@ pub fn virtio_blk_init(node: &Node) { let driver = Arc::new(driver); DRIVERS.write().push(driver.clone()); - BLK_DRIVERS.write().push(Arc::new(BlockDriver(driver))); + BLK_DRIVERS.write().push(driver); } diff --git a/kernel/src/drivers/bus/pci.rs b/kernel/src/drivers/bus/pci.rs index 2744b2bc..94e68a43 100644 --- a/kernel/src/drivers/bus/pci.rs +++ b/kernel/src/drivers/bus/pci.rs @@ -5,7 +5,6 @@ use crate::drivers::{Driver, DRIVERS, NET_DRIVERS}; use crate::memory::active_table; use alloc::collections::BTreeMap; use alloc::sync::Arc; -use core::cmp::Ordering; use pci::*; use rcore_memory::{paging::PageTable, PAGE_SIZE}; use spin::Mutex; @@ -201,7 +200,7 @@ pub fn detach_driver(loc: &Location) -> bool { } pub fn init() { - let mut pci_iter = unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) }; + let pci_iter = unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) }; for dev in pci_iter { info!( "pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) irq: {}:{:?}", @@ -220,7 +219,7 @@ pub fn init() { } pub fn find_device(vendor: u16, product: u16) -> Option { - let mut pci_iter = unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) }; + let pci_iter = unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) }; for dev in pci_iter { if dev.id.vendor_id == vendor && dev.id.device_id == product { return Some(dev.loc); diff --git a/kernel/src/drivers/gpu/fb.rs b/kernel/src/drivers/gpu/fb.rs index 54d7e10a..01fbe6a6 100644 --- a/kernel/src/drivers/gpu/fb.rs +++ b/kernel/src/drivers/gpu/fb.rs @@ -4,7 +4,6 @@ use alloc::string::String; use core::fmt; use lazy_static::lazy_static; use log::*; -use once::*; use spin::Mutex; /// Framebuffer information @@ -134,8 +133,6 @@ impl fmt::Debug for Framebuffer { impl Framebuffer { fn new(width: u32, height: u32, depth: u32) -> Result { - assert_has_not_been_called!("Framebuffer::new must be called only once"); - let probed_info = super::probe_fb_info(width, height, depth); match probed_info { diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index 104a5671..bc9bba8e 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -7,7 +7,7 @@ use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; use spin::RwLock; use crate::sync::Condvar; -use rcore_fs::dev::BlockDevice; +use rcore_fs::dev::{self, BlockDevice, DevError}; #[allow(dead_code)] pub mod block; @@ -72,21 +72,21 @@ pub trait Driver: Send + Sync { } // send an ethernet frame, only use it when necessary - fn send(&self, data: &[u8]) -> Option { + fn send(&self, _data: &[u8]) -> Option { unimplemented!("not a net driver") } // get mac address from ip address in arp table - fn get_arp(&self, ip: IpAddress) -> Option { + fn get_arp(&self, _ip: IpAddress) -> Option { unimplemented!("not a net driver") } // block related drivers should implement these - fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool { + fn read_block(&self, _block_id: usize, _buf: &mut [u8]) -> bool { unimplemented!("not a block driver") } - fn write_block(&self, block_id: usize, buf: &[u8]) -> bool { + fn write_block(&self, _block_id: usize, _buf: &[u8]) -> bool { unimplemented!("not a block driver") } } @@ -95,19 +95,29 @@ lazy_static! { // NOTE: RwLock only write when initializing drivers pub static ref DRIVERS: RwLock>> = RwLock::new(Vec::new()); pub static ref NET_DRIVERS: RwLock>> = RwLock::new(Vec::new()); - pub static ref BLK_DRIVERS: RwLock>> = RwLock::new(Vec::new()); + pub static ref BLK_DRIVERS: RwLock>> = RwLock::new(Vec::new()); } -pub struct BlockDriver(Arc); +pub struct BlockDriver(pub Arc); impl BlockDevice for BlockDriver { const BLOCK_SIZE_LOG2: u8 = 9; // 512 - fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool { - self.0.read_block(block_id, buf) + fn read_at(&self, block_id: usize, buf: &mut [u8]) -> dev::Result<()> { + match self.0.read_block(block_id, buf) { + true => Ok(()), + false => Err(DevError), + } } - fn write_at(&self, block_id: usize, buf: &[u8]) -> bool { - self.0.write_block(block_id, buf) + fn write_at(&self, block_id: usize, buf: &[u8]) -> dev::Result<()> { + match self.0.write_block(block_id, buf) { + true => Ok(()), + false => Err(DevError), + } + } + + fn sync(&self) -> dev::Result<()> { + Ok(()) } } diff --git a/kernel/src/drivers/net/ixgbe.rs b/kernel/src/drivers/net/ixgbe.rs index 115a0bd0..2921a785 100644 --- a/kernel/src/drivers/net/ixgbe.rs +++ b/kernel/src/drivers/net/ixgbe.rs @@ -15,7 +15,6 @@ use smoltcp::wire::EthernetAddress; use smoltcp::wire::*; use smoltcp::Result; -use crate::memory::active_table; use crate::net::SOCKETS; use crate::sync::FlagsGuard; use crate::sync::SpinNoIrqLock as Mutex; @@ -203,7 +202,7 @@ pub fn ixgbe_init( let ip_addrs = [IpCidr::new(IpAddress::v4(10, 0, index as u8, 2), 24)]; let neighbor_cache = NeighborCache::new(BTreeMap::new()); let routes = Routes::new(BTreeMap::new()); - let mut iface = EthernetInterfaceBuilder::new(net_driver.clone()) + let iface = EthernetInterfaceBuilder::new(net_driver.clone()) .ethernet_addr(ethernet_addr) .ip_addrs(ip_addrs) .neighbor_cache(neighbor_cache) diff --git a/kernel/src/drivers/net/virtio_net.rs b/kernel/src/drivers/net/virtio_net.rs index d149ec3e..40c0d312 100644 --- a/kernel/src/drivers/net/virtio_net.rs +++ b/kernel/src/drivers/net/virtio_net.rs @@ -2,7 +2,6 @@ use alloc::alloc::{GlobalAlloc, Layout}; use alloc::format; use alloc::string::String; use alloc::sync::Arc; -use alloc::vec::Vec; use core::mem::size_of; use core::slice; diff --git a/kernel/src/fs/device.rs b/kernel/src/fs/device.rs index dc33ed86..dfe2cc42 100644 --- a/kernel/src/fs/device.rs +++ b/kernel/src/fs/device.rs @@ -19,34 +19,42 @@ impl MemBuf { } impl Device for MemBuf { - fn read_at(&self, offset: usize, buf: &mut [u8]) -> Option { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { let slice = self.0.read(); let len = buf.len().min(slice.len() - offset); buf[..len].copy_from_slice(&slice[offset..offset + len]); - Some(len) + Ok(len) } - fn write_at(&self, offset: usize, buf: &[u8]) -> Option { + fn write_at(&self, offset: usize, buf: &[u8]) -> Result { let mut slice = self.0.write(); let len = buf.len().min(slice.len() - offset); slice[offset..offset + len].copy_from_slice(&buf[..len]); - Some(len) + Ok(len) + } + fn sync(&self) -> Result<()> { + Ok(()) } } #[cfg(target_arch = "x86_64")] impl BlockDevice for ide::IDE { const BLOCK_SIZE_LOG2: u8 = 9; - fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool { + fn read_at(&self, block_id: usize, buf: &mut [u8]) -> Result<()> { use core::slice; assert!(buf.len() >= ide::BLOCK_SIZE); let buf = unsafe { slice::from_raw_parts_mut(buf.as_ptr() as *mut u32, ide::BLOCK_SIZE / 4) }; - self.read(block_id as u64, 1, buf).is_ok() + self.read(block_id as u64, 1, buf).map_err(|_| DevError)?; + Ok(()) } - fn write_at(&self, block_id: usize, buf: &[u8]) -> bool { + fn write_at(&self, block_id: usize, buf: &[u8]) -> Result<()> { use core::slice; assert!(buf.len() >= ide::BLOCK_SIZE); let buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *mut u32, ide::BLOCK_SIZE / 4) }; - self.write(block_id as u64, 1, buf).is_ok() + self.write(block_id as u64, 1, buf).map_err(|_| DevError)?; + Ok(()) + } + fn sync(&self) -> Result<()> { + Ok(()) } } diff --git a/kernel/src/fs/file.rs b/kernel/src/fs/file.rs index 5e1b84f2..aefd349d 100644 --- a/kernel/src/fs/file.rs +++ b/kernel/src/fs/file.rs @@ -1,6 +1,7 @@ //! File handle for process use alloc::{string::String, sync::Arc}; +use core::fmt; use rcore_fs::vfs::{FsError, INode, Metadata, PollStatus, Result}; @@ -9,6 +10,7 @@ pub struct FileHandle { inode: Arc, offset: u64, options: OpenOptions, + pub path: String, } #[derive(Debug, Clone)] @@ -27,12 +29,13 @@ pub enum SeekFrom { } impl FileHandle { - pub fn new(inode: Arc, options: OpenOptions) -> Self { - FileHandle { + pub fn new(inode: Arc, options: OpenOptions, path: String) -> Self { + return FileHandle { inode, offset: 0, options, - } + path, + }; } pub fn read(&mut self, buf: &mut [u8]) -> Result { @@ -116,4 +119,19 @@ impl FileHandle { pub fn io_control(&self, cmd: u32, arg: usize) -> Result<()> { self.inode.io_control(cmd, arg) } + + pub fn inode(&self) -> Arc { + self.inode.clone() + } +} + +impl fmt::Debug for FileHandle { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + return f + .debug_struct("FileHandle") + .field("offset", &self.offset) + .field("options", &self.options) + .field("path", &self.path) + .finish(); + } } diff --git a/kernel/src/fs/file_like.rs b/kernel/src/fs/file_like.rs index 32c5cdcc..ae5ae6b6 100644 --- a/kernel/src/fs/file_like.rs +++ b/kernel/src/fs/file_like.rs @@ -1,5 +1,6 @@ use core::fmt; +use super::ioctl::*; use super::FileHandle; use crate::net::Socket; use crate::syscall::{SysError, SysResult}; @@ -30,13 +31,19 @@ impl FileLike { Ok(len) } pub fn ioctl(&mut self, request: usize, arg1: usize, arg2: usize, arg3: usize) -> SysResult { - match self { - FileLike::File(file) => file.io_control(request as u32, arg1)?, - FileLike::Socket(socket) => { - socket.ioctl(request, arg1, arg2, arg3)?; + match request { + // TODO: place flags & path in FileLike in stead of FileHandle/Socket + FIOCLEX => Ok(0), + _ => { + match self { + FileLike::File(file) => file.io_control(request as u32, arg1)?, + FileLike::Socket(socket) => { + socket.ioctl(request, arg1, arg2, arg3)?; + } + } + Ok(0) } } - Ok(0) } pub fn poll(&self) -> Result { let status = match self { @@ -53,8 +60,8 @@ impl FileLike { impl fmt::Debug for FileLike { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - FileLike::File(_) => write!(f, "File"), - FileLike::Socket(_) => write!(f, "Socket"), + FileLike::File(file) => write!(f, "File({:?})", file), + FileLike::Socket(socket) => write!(f, "Socket({:?})", socket), } } } diff --git a/kernel/src/fs/ioctl.rs b/kernel/src/fs/ioctl.rs new file mode 100644 index 00000000..3eb3509e --- /dev/null +++ b/kernel/src/fs/ioctl.rs @@ -0,0 +1,36 @@ +// for IOR and IOW: +// 32bits total, command in lower 16bits, size of the parameter structure in the lower 14 bits of the upper 16 bits +// higher 2 bits: 01 = write, 10 = read + +#[cfg(not(target_arch = "mips"))] +pub const TCGETS: usize = 0x5401; +#[cfg(target_arch = "mips")] +pub const TCGETS: usize = 0x540D; + +#[cfg(not(target_arch = "mips"))] +pub const TIOCGPGRP: usize = 0x540F; +// _IOR('t', 119, int) +#[cfg(target_arch = "mips")] +pub const TIOCGPGRP: usize = 0x4_004_74_77; + +#[cfg(not(target_arch = "mips"))] +pub const TIOCSPGRP: usize = 0x5410; +// _IOW('t', 118, int) +#[cfg(target_arch = "mips")] +pub const TIOCSPGRP: usize = 0x8_004_74_76; + +#[cfg(not(target_arch = "mips"))] +pub const TIOCGWINSZ: usize = 0x5413; +// _IOR('t', 104, struct winsize) +#[cfg(target_arch = "mips")] +pub const TIOCGWINSZ: usize = 0x4_008_74_68; + +#[cfg(not(target_arch = "mips"))] +pub const FIONCLEX: usize = 0x5450; +#[cfg(target_arch = "mips")] +pub const FIONCLEX: usize = 0x6602; + +#[cfg(not(target_arch = "mips"))] +pub const FIOCLEX: usize = 0x5451; +#[cfg(target_arch = "mips")] +pub const FIOCLEX: usize = 0x6601; diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 1b772a94..0c47c8f6 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -1,20 +1,23 @@ use alloc::{sync::Arc, vec::Vec}; +use rcore_fs::dev::block_cache::BlockCache; use rcore_fs::vfs::*; use rcore_fs_sfs::SimpleFileSystem; -#[cfg(target_arch = "x86_64")] -use crate::arch::driver::ide; +use crate::drivers::BlockDriver; pub use self::file::*; pub use self::file_like::*; pub use self::pipe::Pipe; +pub use self::pseudo::*; pub use self::stdio::{STDIN, STDOUT}; mod device; mod file; mod file_like; +mod ioctl; mod pipe; +mod pseudo; mod stdio; /// Hard link user programs @@ -39,9 +42,15 @@ lazy_static! { let device = { #[cfg(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "x86_64"))] { - crate::drivers::BLK_DRIVERS.read().iter() - .next().expect("Block device not found") - .clone() + let driver = BlockDriver( + crate::drivers::BLK_DRIVERS + .read().iter() + .next().expect("Block device not found") + .clone() + ); + // enable block cache + Arc::new(BlockCache::new(driver, 0x100)) + // Arc::new(driver) } #[cfg(target_arch = "aarch64")] { diff --git a/kernel/src/fs/pipe.rs b/kernel/src/fs/pipe.rs index 88073807..6059e1aa 100644 --- a/kernel/src/fs/pipe.rs +++ b/kernel/src/fs/pipe.rs @@ -57,7 +57,6 @@ impl Pipe { // TODO: better way to provide default impl? macro_rules! impl_inode { () => { - fn poll(&self) -> Result { Err(FsError::NotSupported) } fn metadata(&self) -> Result { Err(FsError::NotSupported) } fn set_metadata(&self, _metadata: &Metadata) -> Result<()> { Ok(()) } fn sync_all(&self) -> Result<()> { Ok(()) } @@ -104,5 +103,41 @@ impl INode for Pipe { Ok(0) } } + + fn poll(&self) -> Result { + let data = self.data.lock(); + match self.direction { + PipeEnd::Read => { + if data.buf.len() > 0 { + Ok(PollStatus { + read: true, + write: false, + error: false, + }) + } else { + Ok(PollStatus { + read: false, + write: false, + error: false, + }) + } + } + PipeEnd::Write => { + if data.buf.len() > 0 { + Ok(PollStatus { + read: false, + write: true, + error: false, + }) + } else { + Ok(PollStatus { + read: false, + write: false, + error: false, + }) + } + } + } + } impl_inode!(); } diff --git a/kernel/src/fs/pseudo.rs b/kernel/src/fs/pseudo.rs new file mode 100644 index 00000000..fdcc5358 --- /dev/null +++ b/kernel/src/fs/pseudo.rs @@ -0,0 +1,78 @@ +//! Pseudo file system INode + +use alloc::{string::String, sync::Arc, vec::Vec}; +use core::any::Any; + +use rcore_fs::vfs::*; + +pub struct Pseudo { + content: Vec, + type_: FileType, +} + +impl Pseudo { + pub fn new(s: &str, type_: FileType) -> Self { + Pseudo { + content: Vec::from(s.as_bytes()), + type_, + } + } +} + +// TODO: better way to provide default impl? +macro_rules! impl_inode { + () => { + fn set_metadata(&self, _metadata: &Metadata) -> Result<()> { Ok(()) } + fn sync_all(&self) -> Result<()> { Ok(()) } + fn sync_data(&self) -> Result<()> { Ok(()) } + fn resize(&self, _len: usize) -> Result<()> { Err(FsError::NotSupported) } + fn create(&self, _name: &str, _type_: FileType, _mode: u32) -> Result> { Err(FsError::NotDir) } + fn unlink(&self, _name: &str) -> Result<()> { Err(FsError::NotDir) } + fn link(&self, _name: &str, _other: &Arc) -> Result<()> { Err(FsError::NotDir) } + fn move_(&self, _old_name: &str, _target: &Arc, _new_name: &str) -> Result<()> { Err(FsError::NotDir) } + fn find(&self, _name: &str) -> Result> { Err(FsError::NotDir) } + fn get_entry(&self, _id: usize) -> Result { Err(FsError::NotDir) } + fn io_control(&self, cmd: u32, data: usize) -> Result<()> { Err(FsError::NotSupported) } + fn fs(&self) -> Arc { unimplemented!() } + fn as_any_ref(&self) -> &Any { self } + }; +} + +impl INode for Pseudo { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { + if offset >= self.content.len() { + return Ok(0); + } + let len = (self.content.len() - offset).min(buf.len()); + buf[..len].copy_from_slice(&self.content[offset..offset + len]); + Ok(len) + } + fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result { + Err(FsError::NotSupported) + } + fn poll(&self) -> Result { + Ok(PollStatus { + read: true, + write: false, + error: false, + }) + } + fn metadata(&self) -> Result { + Ok(Metadata { + dev: 0, + inode: 0, + size: self.content.len(), + blk_size: 0, + blocks: 0, + atime: Timespec { sec: 0, nsec: 0 }, + mtime: Timespec { sec: 0, nsec: 0 }, + ctime: Timespec { sec: 0, nsec: 0 }, + type_: self.type_, + mode: 0, + nlinks: 0, + uid: 0, + gid: 0, + }) + } + impl_inode!(); +} diff --git a/kernel/src/fs/stdio.rs b/kernel/src/fs/stdio.rs index 28bb9346..d13520ae 100644 --- a/kernel/src/fs/stdio.rs +++ b/kernel/src/fs/stdio.rs @@ -5,6 +5,7 @@ use core::any::Any; use rcore_fs::vfs::*; +use super::ioctl::*; use crate::sync::Condvar; use crate::sync::SpinNoIrqLock as Mutex; @@ -20,16 +21,39 @@ impl Stdin { self.pushed.notify_one(); } pub fn pop(&self) -> char { + #[cfg(feature = "board_k210")] loop { - let ret = self.buf.lock().pop_front(); - match ret { + // polling + let c = crate::arch::io::getchar(); + if c != '\0' { + return c; + } + } + #[cfg(feature = "board_rocket_chip")] + loop { + let c = crate::arch::io::getchar(); + if c != '\0' && c as u8 != 254 { + return c; + } + } + #[cfg(not(any(feature = "board_k210", feature = "board_rocket_chip")))] + loop { + let mut buf_lock = self.buf.lock(); + match buf_lock.pop_front() { Some(c) => return c, - None => self.pushed._wait(), + None => { + self.pushed.wait(buf_lock); + } } } } pub fn can_read(&self) -> bool { - self.buf.lock().len() > 0 + // Currently, rocket-chip implementation rely on htif interface, the serial interrupt DO + // NOT work, so return true always + #[cfg(feature = "board_rocket_chip")] + return true; + #[cfg(not(feature = "board_rocket_chip"))] + return self.buf.lock().len() > 0; } } @@ -41,32 +65,6 @@ lazy_static! { pub static ref STDOUT: Arc = Arc::new(Stdout::default()); } -// 32bits total, command in lower 16bits, size of the parameter structure in the lower 14 bits of the upper 16 bits -// higher 2 bits: 01 = write, 10 = read - -#[cfg(not(target_arch = "mips"))] -const TCGETS: u32 = 0x5401; -#[cfg(target_arch = "mips")] -const TCGETS: u32 = 0x540D; - -#[cfg(not(target_arch = "mips"))] -const TIOCGPGRP: u32 = 0x540F; -// _IOR('t', 119, int) -#[cfg(target_arch = "mips")] -const TIOCGPGRP: u32 = 0x4_004_74_77; - -#[cfg(not(target_arch = "mips"))] -const TIOCSPGRP: u32 = 0x5410; -// _IOW('t', 118, int) -#[cfg(target_arch = "mips")] -const TIOCSPGRP: u32 = 0x8_004_74_76; - -#[cfg(not(target_arch = "mips"))] -const TIOCGWINSZ: u32 = 0x5413; -// _IOR('t', 104, struct winsize) -#[cfg(target_arch = "mips")] -const TIOCGWINSZ: u32 = 0x4_008_74_68; - // TODO: better way to provide default impl? macro_rules! impl_inode { () => { @@ -82,7 +80,7 @@ macro_rules! impl_inode { fn find(&self, _name: &str) -> Result> { Err(FsError::NotDir) } fn get_entry(&self, _id: usize) -> Result { Err(FsError::NotDir) } fn io_control(&self, cmd: u32, data: usize) -> Result<()> { - match cmd { + match cmd as usize { TCGETS | TIOCGWINSZ | TIOCSPGRP => { // pretend to be tty Ok(()) diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index fa5f0a65..b6c482c1 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -18,7 +18,7 @@ extern crate log; extern crate lazy_static; pub use crate::process::{new_kernel_context, processor}; -use buddy_system_allocator::LockedHeap; +use buddy_system_allocator::LockedHeapWithRescue; use rcore_thread::std_thread as thread; #[macro_use] // print! @@ -65,4 +65,5 @@ pub fn kmain() -> ! { /// /// It should be defined in memory mod, but in Rust `global_allocator` must be in root mod. #[global_allocator] -static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); +static HEAP_ALLOCATOR: LockedHeapWithRescue = + LockedHeapWithRescue::new(crate::memory::enlarge_heap); diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 54c2cff3..8c9aeb3c 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -1,14 +1,30 @@ +//! Define the FrameAllocator for physical memory +//! x86_64 -- 64GB +//! AARCH64/MIPS/RV -- 1GB +//! K210(rv64) -- 8MB +//! NOTICE: +//! type FrameAlloc = bitmap_allocator::BitAllocXXX +//! KSTACK_SIZE -- 16KB +//! +//! KERNEL_HEAP_SIZE: +//! x86-64 -- 32MB +//! AARCH64/RV64 -- 8MB +//! MIPS/RV32 -- 2MB +//! mipssim/malta(MIPS) -- 10MB + use super::HEAP_ALLOCATOR; pub use crate::arch::paging::*; -use crate::consts::MEMORY_OFFSET; -use crate::process::process_unsafe; +use crate::consts::{KERNEL_OFFSET, MEMORY_OFFSET}; use crate::sync::SpinNoIrqLock; +use alloc::boxed::Box; use bitmap_allocator::BitAlloc; -use buddy_system_allocator::LockedHeap; +use buddy_system_allocator::Heap; use lazy_static::*; use log::*; pub use rcore_memory::memory_set::{handler::*, MemoryArea, MemoryAttr}; +use rcore_memory::paging::PageTable; use rcore_memory::*; +use crate::process::current_thread; pub type MemorySet = rcore_memory::memory_set::MemorySet; @@ -16,13 +32,21 @@ pub type MemorySet = rcore_memory::memory_set::MemorySet; #[cfg(target_arch = "x86_64")] pub type FrameAlloc = bitmap_allocator::BitAlloc16M; -// RISCV has 1G memory -#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +// RISCV, ARM, MIPS has 1G memory +#[cfg(all( + any( + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "aarch64", + target_arch = "mips" + ), + not(feature = "board_k210") +))] pub type FrameAlloc = bitmap_allocator::BitAlloc1M; -// Raspberry Pi 3 has 1G memory -#[cfg(any(target_arch = "aarch64", target_arch = "mips"))] -pub type FrameAlloc = bitmap_allocator::BitAlloc1M; +// K210 has 8M memory +#[cfg(feature = "board_k210")] +pub type FrameAlloc = bitmap_allocator::BitAlloc4K; lazy_static! { pub static ref FRAME_ALLOCATOR: SpinNoIrqLock = @@ -77,17 +101,17 @@ pub fn dealloc_frame(target: usize) { } pub struct KernelStack(usize); -const STACK_SIZE: usize = 0x8000; +const KSTACK_SIZE: usize = 0x4000; //16KB impl KernelStack { pub fn new() -> Self { use alloc::alloc::{alloc, Layout}; let bottom = - unsafe { alloc(Layout::from_size_align(STACK_SIZE, STACK_SIZE).unwrap()) } as usize; + unsafe { alloc(Layout::from_size_align(KSTACK_SIZE, KSTACK_SIZE).unwrap()) } as usize; KernelStack(bottom) } pub fn top(&self) -> usize { - self.0 + STACK_SIZE + self.0 + KSTACK_SIZE } } @@ -97,7 +121,7 @@ impl Drop for KernelStack { unsafe { dealloc( self.0 as _, - Layout::from_size_align(STACK_SIZE, STACK_SIZE).unwrap(), + Layout::from_size_align(KSTACK_SIZE, KSTACK_SIZE).unwrap(), ); } } @@ -108,8 +132,8 @@ impl Drop for KernelStack { pub fn handle_page_fault(addr: usize) -> bool { // debug!("page fault @ {:#x}", addr); - // This is safe as long as page fault never happens in page fault handler - unsafe { process_unsafe().vm.handle_page_fault(addr) } + let thread = unsafe { current_thread() }; + thread.vm.lock().handle_page_fault(addr) } pub fn init_heap() { @@ -123,5 +147,37 @@ pub fn init_heap() { info!("heap init end"); } -/// Allocator for the rest memory space on NO-MMU case. -pub static MEMORY_ALLOCATOR: LockedHeap = LockedHeap::empty(); +pub fn enlarge_heap(heap: &mut Heap) { + info!("Enlarging heap to avoid oom"); + + let mut page_table = active_table(); + let mut addrs = [(0, 0); 32]; + let mut addr_len = 0; + #[cfg(target_arch = "x86_64")] + let va_offset = KERNEL_OFFSET + 0xe0000000; + #[cfg(not(target_arch = "x86_64"))] + let va_offset = KERNEL_OFFSET + 0x00e00000; + for i in 0..16384 { + let page = alloc_frame().unwrap(); + let va = va_offset + page; + if addr_len > 0 { + let (ref mut addr, ref mut len) = addrs[addr_len - 1]; + if *addr - PAGE_SIZE == va { + *len += PAGE_SIZE; + *addr -= PAGE_SIZE; + continue; + } + } + addrs[addr_len] = (va, PAGE_SIZE); + addr_len += 1; + } + for (addr, len) in addrs[..addr_len].into_iter() { + for va in (*addr..(*addr + *len)).step_by(PAGE_SIZE) { + page_table.map(va, va - va_offset).update(); + } + info!("Adding {:#X} {:#X} to heap", addr, len); + unsafe { + heap.init(*addr, *len); + } + } +} diff --git a/kernel/src/net/structs.rs b/kernel/src/net/structs.rs index 7dcb7545..544b87ce 100644 --- a/kernel/src/net/structs.rs +++ b/kernel/src/net/structs.rs @@ -4,6 +4,7 @@ use crate::sync::SpinNoIrqLock as Mutex; use crate::syscall::*; use crate::util; use alloc::boxed::Box; +use alloc::fmt::Debug; use alloc::sync::Arc; use alloc::vec::Vec; use bitflags::*; @@ -50,12 +51,12 @@ pub enum Endpoint { } /// Common methods that a socket must have -pub trait Socket: Send + Sync { +pub trait Socket: Send + Sync + Debug { fn read(&self, data: &mut [u8]) -> (SysResult, Endpoint); fn write(&self, data: &[u8], sendto_endpoint: Option) -> SysResult; fn poll(&self) -> (bool, bool, bool); // (in, out, err) fn connect(&mut self, endpoint: Endpoint) -> SysResult; - fn bind(&mut self, endpoint: Endpoint) -> SysResult { + fn bind(&mut self, _endpoint: Endpoint) -> SysResult { Err(SysError::EINVAL) } fn listen(&mut self) -> SysResult { @@ -73,11 +74,11 @@ pub trait Socket: Send + Sync { fn remote_endpoint(&self) -> Option { None } - fn setsockopt(&mut self, level: usize, opt: usize, data: &[u8]) -> SysResult { + fn setsockopt(&mut self, _level: usize, _opt: usize, _data: &[u8]) -> SysResult { warn!("setsockopt is unimplemented"); Ok(0) } - fn ioctl(&mut self, request: usize, arg1: usize, arg2: usize, arg3: usize) -> SysResult { + fn ioctl(&mut self, _request: usize, _arg1: usize, _arg2: usize, _arg3: usize) -> SysResult { warn!("ioctl is unimplemented for this socket"); Ok(0) } @@ -265,9 +266,8 @@ impl Socket for TcpSocketState { TcpState::SynSent => { // still connecting drop(socket); - drop(sockets); debug!("poll for connection wait"); - SOCKET_ACTIVITY._wait(); + SOCKET_ACTIVITY.wait(sockets); } TcpState::Established => { break Ok(0); @@ -285,7 +285,7 @@ impl Socket for TcpSocketState { } } - fn bind(&mut self, mut endpoint: Endpoint) -> SysResult { + fn bind(&mut self, endpoint: Endpoint) -> SysResult { if let Endpoint::Ip(mut ip) = endpoint { if ip.port == 0 { ip.port = get_ephemeral_port(); @@ -357,10 +357,8 @@ impl Socket for TcpSocketState { return Ok((new_socket, Endpoint::Ip(remote_endpoint))); } - // avoid deadlock drop(socket); - drop(sockets); - SOCKET_ACTIVITY._wait(); + SOCKET_ACTIVITY.wait(sockets); } } @@ -447,9 +445,8 @@ impl Socket for UdpSocketState { ); } - // avoid deadlock drop(socket); - SOCKET_ACTIVITY._wait() + SOCKET_ACTIVITY.wait(sockets); } } @@ -626,10 +623,8 @@ impl Socket for RawSocketState { ); } - // avoid deadlock drop(socket); - drop(sockets); - SOCKET_ACTIVITY._wait() + SOCKET_ACTIVITY.wait(sockets); } } @@ -941,7 +936,7 @@ impl Socket for NetlinkSocketState { let ifaces = NET_DRIVERS.read(); for i in 0..ifaces.len() { let mut msg = Vec::new(); - let mut new_header = NetlinkMessageHeader { + let new_header = NetlinkMessageHeader { nlmsg_len: 0, // to be determined later nlmsg_type: NetlinkMessageType::NewLink.into(), nlmsg_flags: NetlinkMessageFlags::MULTI, @@ -999,7 +994,7 @@ impl Socket for NetlinkSocketState { let ip_addrs = ifaces[i].get_ip_addresses(); for j in 0..ip_addrs.len() { let mut msg = Vec::new(); - let mut new_header = NetlinkMessageHeader { + let new_header = NetlinkMessageHeader { nlmsg_len: 0, // to be determined later nlmsg_type: NetlinkMessageType::NewAddr.into(), nlmsg_flags: NetlinkMessageFlags::MULTI, @@ -1045,7 +1040,7 @@ impl Socket for NetlinkSocketState { _ => {} } let mut msg = Vec::new(); - let mut new_header = NetlinkMessageHeader { + let new_header = NetlinkMessageHeader { nlmsg_len: 0, // to be determined later nlmsg_type: NetlinkMessageType::Done.into(), nlmsg_flags: NetlinkMessageFlags::MULTI, diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 516d29e2..494f7b7b 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -20,7 +20,7 @@ pub fn init() { } } - crate::shell::run_user_shell(); + crate::shell::add_user_shell(); info!("process: init end"); } @@ -109,25 +109,13 @@ static PROCESSORS: [Processor; MAX_CPU_NUM] = [ // Processor::new(), Processor::new(), Processor::new(), Processor::new(), ]; -/// Get current process -pub fn process() -> MutexGuard<'static, Process, SpinNoIrq> { - current_thread().proc.lock() -} - -/// Get current process, ignoring its lock -/// Only use this when necessary -pub unsafe fn process_unsafe() -> MutexGuard<'static, Process, SpinNoIrq> { - let thread = current_thread(); - thread.proc.force_unlock(); - thread.proc.lock() -} - /// Get current thread /// -/// FIXME: It's obviously unsafe to get &mut ! -pub fn current_thread() -> &'static mut Thread { - use core::mem::transmute; - let (process, _): (&mut Thread, *const ()) = unsafe { transmute(processor().context()) }; +/// `Thread` is a thread-local object. +/// It is safe to call this once, and pass `&mut Thread` as a function argument. +pub unsafe fn current_thread() -> &'static mut Thread { + // trick: force downcast from trait object + let (process, _): (&mut Thread, *const ()) = core::mem::transmute(processor().context()); process } diff --git a/kernel/src/process/structs.rs b/kernel/src/process/structs.rs index a9982ee8..3f7c2720 100644 --- a/kernel/src/process/structs.rs +++ b/kernel/src/process/structs.rs @@ -13,68 +13,55 @@ use xmas_elf::{ }; use crate::arch::interrupt::{Context, TrapFrame}; -use crate::fs::{FileHandle, FileLike, INodeExt, OpenOptions, FOLLOW_MAX_DEPTH}; -use crate::memory::{ByFrame, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet}; -use crate::net::SOCKETS; +use crate::fs::{FileHandle, FileLike, OpenOptions, FOLLOW_MAX_DEPTH}; +use crate::memory::{ + ByFrame, Delay, File, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet, Read, +}; use crate::sync::{Condvar, SpinNoIrqLock as Mutex}; use super::abi::{self, ProcInitInfo}; +use core::mem::uninitialized; +use rcore_fs::vfs::INode; -// TODO: avoid pub pub struct Thread { - pub context: Context, - pub kstack: KernelStack, + context: Context, + kstack: KernelStack, /// Kernel performs futex wake when thread exits. /// Ref: [http://man7.org/linux/man-pages/man2/set_tid_address.2.html] pub clear_child_tid: usize, + // This is same as `proc.vm` + pub vm: Arc>, pub proc: Arc>, } /// Pid type /// For strong type separation -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Pid(Option); +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Pid(usize); impl Pid { - pub fn uninitialized() -> Self { - Pid(None) - } - - /// Return if it was uninitialized before this call - /// When returning true, it usually means this is the first thread - pub fn set_if_uninitialized(&mut self, tid: Tid) -> bool { - if self.0 == None { - self.0 = Some(tid as usize); - true - } else { - false - } - } - pub fn get(&self) -> usize { - self.0.unwrap() + self.0 } /// Return whether this pid represents the init process pub fn is_init(&self) -> bool { - self.0 == Some(0) + self.0 == 0 } } impl fmt::Display for Pid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - Some(pid) => write!(f, "{}", pid), - None => write!(f, "None"), - } + write!(f, "{}", self.0) } } pub struct Process { // resources - pub vm: MemorySet, + pub vm: Arc>, pub files: BTreeMap, pub cwd: String, + pub exec_path: String, futexes: BTreeMap>, // relationship @@ -103,21 +90,9 @@ impl rcore_thread::Context for Thread { } fn set_tid(&mut self, tid: Tid) { - // set pid=tid if unspecified let mut proc = self.proc.lock(); - if proc.pid.set_if_uninitialized(tid) { - // first thread in the process - // link to its ppid - if let Some(parent) = &proc.parent { - let mut parent = parent.lock(); - parent.children.push(Arc::downgrade(&self.proc)); - } - } // add it to threads proc.threads.push(tid); - PROCESSES - .write() - .insert(proc.pid.get(), Arc::downgrade(&self.proc)); } } @@ -126,52 +101,63 @@ impl Thread { pub unsafe fn new_init() -> Box { Box::new(Thread { context: Context::null(), - kstack: KernelStack::new(), - clear_child_tid: 0, - // safety: this field will never be used - proc: core::mem::uninitialized(), + // safety: other fields will never be used + ..core::mem::uninitialized() }) } /// Make a new kernel thread starting from `entry` with `arg` pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Box { let vm = MemorySet::new(); + let vm_token = vm.token(); + let vm = Arc::new(Mutex::new(vm)); let kstack = KernelStack::new(); Box::new(Thread { - context: unsafe { Context::new_kernel_thread(entry, arg, kstack.top(), vm.token()) }, + context: unsafe { Context::new_kernel_thread(entry, arg, kstack.top(), vm_token) }, kstack, clear_child_tid: 0, + vm: vm.clone(), // TODO: kernel thread should not have a process - proc: Arc::new(Mutex::new(Process { + proc: Process { vm, files: BTreeMap::default(), cwd: String::from("/"), + exec_path: String::new(), futexes: BTreeMap::default(), - pid: Pid::uninitialized(), + pid: Pid(0), parent: None, children: Vec::new(), threads: Vec::new(), child_exit: Arc::new(Condvar::new()), child_exit_code: BTreeMap::new(), - })), + } + .add_to_table(), }) } - /// Make a new user process from ELF `data` - pub fn new_user( - data: &[u8], + /// Construct virtual memory of a new user process from ELF `data`. + /// Return `(MemorySet, entry_point, ustack_top)` + pub fn new_user_vm( + inode: &Arc, exec_path: &str, mut args: Vec, envs: Vec, - ) -> Box { + ) -> Result<(MemorySet, usize, usize), &'static str> { + // Read ELF header + // 0x3c0: magic number from ld-musl.so + let mut data: [u8; 0x3c0] = unsafe { uninitialized() }; + inode + .read_at(0, &mut data) + .map_err(|_| "failed to read from INode")?; + // Parse ELF - let elf = ElfFile::new(data).expect("failed to read elf"); + let elf = ElfFile::new(&data)?; // Check ELF type match elf.header.pt2.type_().as_type() { header::Type::Executable => {} header::Type::SharedObject => {} - _ => panic!("ELF is not executable or shared object"), + _ => return Err("ELF is not executable or shared object"), } // Check ELF arch @@ -184,31 +170,25 @@ impl Thread { header::Machine::Other(243) => {} #[cfg(target_arch = "mips")] header::Machine::Mips => {} - machine @ _ => panic!("invalid elf arch: {:?}", machine), + _ => return Err("invalid ELF arch"), } // Check interpreter (for dynamic link) if let Ok(loader_path) = elf.get_interpreter() { // assuming absolute path - if let Ok(inode) = crate::fs::ROOT_INODE.lookup_follow(loader_path, FOLLOW_MAX_DEPTH) { - if let Ok(buf) = inode.read_as_vec() { - // Elf loader should not have INTERP - // No infinite loop - args.insert(0, loader_path.into()); - args.insert(1, exec_path.into()); - args.remove(2); - info!("loader args: {:?}", args); - return Thread::new_user(buf.as_slice(), exec_path, args, envs); - } else { - warn!("loader specified as {} but failed to read", &loader_path); - } - } else { - warn!("loader specified as {} but not found", &loader_path); - } + let inode = crate::fs::ROOT_INODE + .lookup_follow(loader_path, FOLLOW_MAX_DEPTH) + .map_err(|_| "interpreter not found")?; + // modify args for loader + args[0] = exec_path.into(); + args.insert(0, loader_path.into()); + // Elf loader should not have INTERP + // No infinite loop + return Thread::new_user_vm(&inode, exec_path, args, envs); } // Make page table - let mut vm = elf.make_memory_set(); + let mut vm = elf.make_memory_set(inode); // User stack use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE}; @@ -217,6 +197,14 @@ impl Thread { let ustack_top = USER_STACK_OFFSET + USER_STACK_SIZE; vm.push( ustack_buttom, + ustack_top - PAGE_SIZE * 4, + MemoryAttr::default().user(), + Delay::new(GlobalFrameAlloc), + "user_stack_delay", + ); + // We are going to write init info now. So map the last 4 pages eagerly. + vm.push( + ustack_top - PAGE_SIZE * 4, ustack_top, MemoryAttr::default().user(), ByFrame::new(GlobalFrameAlloc), @@ -246,6 +234,21 @@ impl Thread { trace!("{:#x?}", vm); + let entry_addr = elf.header.pt2.entry_point() as usize; + Ok((vm, entry_addr, ustack_top)) + } + + /// Make a new user process from ELF `data` + pub fn new_user( + inode: &Arc, + exec_path: &str, + args: Vec, + envs: Vec, + ) -> Box { + let (vm, entry_addr, ustack_top) = Self::new_user_vm(inode, exec_path, args, envs).unwrap(); + + let vm_token = vm.token(); + let vm = Arc::new(Mutex::new(vm)); let kstack = KernelStack::new(); let mut files = BTreeMap::new(); @@ -258,6 +261,7 @@ impl Thread { write: false, append: false, }, + String::from("stdin"), )), ); files.insert( @@ -269,6 +273,7 @@ impl Thread { write: true, append: false, }, + String::from("stdout"), )), ); files.insert( @@ -280,69 +285,66 @@ impl Thread { write: true, append: false, }, + String::from("stderr"), )), ); - let entry_addr = elf.header.pt2.entry_point() as usize; - Box::new(Thread { context: unsafe { - Context::new_user_thread(entry_addr, ustack_top, kstack.top(), vm.token()) + Context::new_user_thread(entry_addr, ustack_top, kstack.top(), vm_token) }, kstack, clear_child_tid: 0, - proc: Arc::new(Mutex::new(Process { + vm: vm.clone(), + proc: Process { vm, files, cwd: String::from("/"), + exec_path: String::from(exec_path), futexes: BTreeMap::default(), - pid: Pid::uninitialized(), + pid: Pid(0), parent: None, children: Vec::new(), threads: Vec::new(), child_exit: Arc::new(Condvar::new()), child_exit_code: BTreeMap::new(), - })), + } + .add_to_table(), }) } /// Fork a new process from current one pub fn fork(&self, tf: &TrapFrame) -> Box { - // Clone memory set, make a new page table - let proc = self.proc.lock(); - let vm = proc.vm.clone(); - let files = proc.files.clone(); - let cwd = proc.cwd.clone(); - drop(proc); - let parent = Some(self.proc.clone()); - debug!("fork: finish clone MemorySet"); - - // MMU: copy data to the new space - // NoMMU: coping data has been done in `vm.clone()` - for area in vm.iter() { - let data = Vec::::from(unsafe { area.as_slice() }); - unsafe { vm.with(|| area.as_slice_mut().copy_from_slice(data.as_slice())) } - } - - debug!("fork: temporary copy data!"); let kstack = KernelStack::new(); + let vm = self.vm.lock().clone(); + let vm_token = vm.token(); + let vm = Arc::new(Mutex::new(vm)); + let context = unsafe { Context::new_fork(tf, kstack.top(), vm_token) }; + + let mut proc = self.proc.lock(); + let new_proc = Process { + vm: vm.clone(), + files: proc.files.clone(), + cwd: proc.cwd.clone(), + exec_path: proc.exec_path.clone(), + futexes: BTreeMap::default(), + pid: Pid(0), + parent: Some(self.proc.clone()), + children: Vec::new(), + threads: Vec::new(), + child_exit: Arc::new(Condvar::new()), + child_exit_code: BTreeMap::new(), + } + .add_to_table(); + // link to parent + proc.children.push(Arc::downgrade(&new_proc)); Box::new(Thread { - context: unsafe { Context::new_fork(tf, kstack.top(), vm.token()) }, + context, kstack, clear_child_tid: 0, - proc: Arc::new(Mutex::new(Process { - vm, - files, - cwd, - futexes: BTreeMap::default(), - pid: Pid::uninitialized(), - parent, - children: Vec::new(), - threads: Vec::new(), - child_exit: Arc::new(Condvar::new()), - child_exit_code: BTreeMap::new(), - })), + vm, + proc: new_proc, }) } @@ -355,33 +357,52 @@ impl Thread { clear_child_tid: usize, ) -> Box { let kstack = KernelStack::new(); - let token = self.proc.lock().vm.token(); + let vm_token = self.vm.lock().token(); Box::new(Thread { - context: unsafe { Context::new_clone(tf, stack_top, kstack.top(), token, tls) }, + context: unsafe { Context::new_clone(tf, stack_top, kstack.top(), vm_token, tls) }, kstack, clear_child_tid, + vm: self.vm.clone(), proc: self.proc.clone(), }) } } impl Process { - pub fn get_free_fd(&self) -> usize { + /// Assign a pid and put itself to global process table. + fn add_to_table(mut self) -> Arc> { + let mut process_table = PROCESSES.write(); + + // assign pid + let pid = (0..) + .find(|i| match process_table.get(i) { + Some(p) if p.upgrade().is_some() => false, + _ => true, + }) + .unwrap(); + self.pid = Pid(pid); + + // put to process table + let self_ref = Arc::new(Mutex::new(self)); + process_table.insert(pid, Arc::downgrade(&self_ref)); + + self_ref + } + fn get_free_fd(&self) -> usize { (0..).find(|i| !self.files.contains_key(i)).unwrap() } + /// Add a file to the process, return its fd. + pub fn add_file(&mut self, file_like: FileLike) -> usize { + let fd = self.get_free_fd(); + self.files.insert(fd, file_like); + fd + } pub fn get_futex(&mut self, uaddr: usize) -> Arc { if !self.futexes.contains_key(&uaddr) { self.futexes.insert(uaddr, Arc::new(Condvar::new())); } self.futexes.get(&uaddr).unwrap().clone() } - pub fn clone_for_exec(&mut self, other: &Self) { - self.files = other.files.clone(); - self.cwd = other.cwd.clone(); - self.pid = other.pid.clone(); - self.parent = other.parent.clone(); - self.threads = other.threads.clone(); - } } trait ToMemoryAttr { @@ -391,10 +412,12 @@ trait ToMemoryAttr { impl ToMemoryAttr for Flags { fn to_attr(&self) -> MemoryAttr { let mut flags = MemoryAttr::default().user(); - // FIXME: handle readonly if self.is_execute() { flags = flags.execute(); } + if !self.is_write() { + flags = flags.readonly(); + } flags } } @@ -402,7 +425,7 @@ impl ToMemoryAttr for Flags { /// Helper functions to process ELF file trait ElfExt { /// Generate a MemorySet according to the ELF file. - fn make_memory_set(&self) -> MemorySet; + fn make_memory_set(&self, inode: &Arc) -> MemorySet; /// Get interpreter string if it has. fn get_interpreter(&self) -> Result<&str, &str>; @@ -412,7 +435,7 @@ trait ElfExt { } impl ElfExt for ElfFile<'_> { - fn make_memory_set(&self) -> MemorySet { + fn make_memory_set(&self, inode: &Arc) -> MemorySet { debug!("creating MemorySet from ELF"); let mut ms = MemorySet::new(); @@ -420,33 +443,19 @@ impl ElfExt for ElfFile<'_> { if ph.get_type() != Ok(Type::Load) { continue; } - let virt_addr = ph.virtual_addr() as usize; - let mem_size = ph.mem_size() as usize; - let data = match ph.get_data(self).unwrap() { - SegmentData::Undefined(data) => data, - _ => unreachable!(), - }; - - // Get target slice - let target = { - ms.push( - virt_addr, - virt_addr + mem_size, - ph.flags().to_attr(), - ByFrame::new(GlobalFrameAlloc), - "elf", - ); - unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) } - }; - // Copy data - unsafe { - ms.with(|| { - if data.len() != 0 { - target[..data.len()].copy_from_slice(data); - } - target[data.len()..].iter_mut().for_each(|x| *x = 0); - }); - } + ms.push( + ph.virtual_addr() as usize, + ph.virtual_addr() as usize + ph.mem_size() as usize, + ph.flags().to_attr(), + File { + file: INodeForMap(inode.clone()), + mem_start: ph.virtual_addr() as usize, + file_start: ph.offset() as usize, + file_end: ph.offset() as usize + ph.file_size() as usize, + allocator: GlobalFrameAlloc, + }, + "elf", + ); } ms } @@ -488,3 +497,12 @@ impl ElfExt for ElfFile<'_> { } } } + +#[derive(Clone)] +pub struct INodeForMap(pub Arc); + +impl Read for INodeForMap { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize { + self.0.read_at(offset, buf).unwrap() + } +} diff --git a/kernel/src/shell.rs b/kernel/src/shell.rs index 000ad633..7004ae96 100644 --- a/kernel/src/shell.rs +++ b/kernel/src/shell.rs @@ -1,33 +1,51 @@ //! Kernel shell -use crate::drivers::CMDLINE; -use crate::fs::{INodeExt, ROOT_INODE}; +use crate::arch::io; +use crate::fs::ROOT_INODE; use crate::process::*; use alloc::string::String; use alloc::vec::Vec; #[cfg(not(feature = "run_cmdline"))] -pub fn run_user_shell() { - if let Ok(inode) = ROOT_INODE.lookup("busybox") { - let data = inode.read_as_vec().unwrap(); - processor().manager().add(Thread::new_user( - data.as_slice(), - "busybox", - vec!["busybox".into(), "sh".into()], - Vec::new(), - )); +pub fn add_user_shell() { + // the busybox of alpine linux can not transfer env vars into child process + // Now we use busybox from + // https://raw.githubusercontent.com/docker-library/busybox/82bc0333a9ae148fbb4246bcbff1487b3fc0c510/musl/busybox.tar.xz -O busybox.tar.xz + // This one can transfer env vars! + // Why??? + + // #[cfg(target_arch = "x86_64")] + // let init_shell="/bin/busybox"; // from alpine linux + // + // #[cfg(not(target_arch = "x86_64"))] + let init_shell = "/busybox"; //from docker-library + + #[cfg(target_arch = "x86_64")] + let init_envs = + vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/x86_64-alpine-linux-musl/bin".into()]; + + #[cfg(not(target_arch = "x86_64"))] + let init_envs = Vec::new(); + + let init_args = vec!["busybox".into(), "ash".into()]; + + if let Ok(inode) = ROOT_INODE.lookup(init_shell) { + processor() + .manager() + .add(Thread::new_user(&inode, init_shell, init_args, init_envs)); } else { processor().manager().add(Thread::new_kernel(shell, 0)); } } #[cfg(feature = "run_cmdline")] -pub fn run_user_shell() { +pub fn add_user_shell() { + use crate::drivers::CMDLINE; let cmdline = CMDLINE.read(); let inode = ROOT_INODE.lookup(&cmdline).unwrap(); - let data = inode.read_as_vec().unwrap(); processor().manager().add(Thread::new_user( - data.as_slice(), + &inode, + &cmdline, cmdline.split(' ').map(|s| s.into()).collect(), Vec::new(), )); @@ -45,16 +63,14 @@ pub extern "C" fn shell(_arg: usize) -> ! { continue; } let name = cmd.trim().split(' ').next().unwrap(); - if let Ok(file) = ROOT_INODE.lookup(name) { - let data = file.read_as_vec().unwrap(); - let _pid = processor().manager().add(Thread::new_user( - data.as_slice(), + if let Ok(inode) = ROOT_INODE.lookup(name) { + let _tid = processor().manager().add(Thread::new_user( + &inode, &cmd, cmd.split(' ').map(|s| s.into()).collect(), Vec::new(), )); // TODO: wait until process exits, or use user land shell completely - //unsafe { thread::JoinHandle::<()>::_of(pid) }.join().unwrap(); } else { println!("Program not exist"); } diff --git a/kernel/src/sync/condvar.rs b/kernel/src/sync/condvar.rs index a03f1073..61db0737 100644 --- a/kernel/src/sync/condvar.rs +++ b/kernel/src/sync/condvar.rs @@ -15,6 +15,7 @@ impl Condvar { } /// Park current thread and wait for this condvar to be notified. + #[deprecated(note = "this may leads to lost wakeup problem. please use `wait` instead.")] pub fn _wait(&self) { // The condvar might be notified between adding to queue and thread parking. // So park current thread before wait queue lock is freed. @@ -25,6 +26,7 @@ impl Condvar { }); } + #[deprecated(note = "this may leads to lost wakeup problem. please use `wait` instead.")] pub fn wait_any(condvars: &[&Condvar]) { let token = Arc::new(thread::current()); // Avoid racing in the same way as the function above @@ -40,19 +42,23 @@ impl Condvar { }); } - pub fn add_to_wait_queue(&self) -> MutexGuard>, SpinNoIrq> { + fn add_to_wait_queue(&self) -> MutexGuard>, SpinNoIrq> { let mut lock = self.wait_queue.lock(); lock.push_back(Arc::new(thread::current())); return lock; } + /// Park current thread and wait for this condvar to be notified. pub fn wait<'a, T, S>(&self, guard: MutexGuard<'a, T, S>) -> MutexGuard<'a, T, S> where S: MutexSupport, { let mutex = guard.mutex; - drop(guard); - self._wait(); + let lock = self.add_to_wait_queue(); + thread::park_action(move || { + drop(lock); + drop(guard); + }); mutex.lock() } @@ -80,7 +86,4 @@ impl Condvar { } count } - pub fn _clear(&self) { - self.wait_queue.lock().clear(); - } } diff --git a/kernel/src/syscall/custom.rs b/kernel/src/syscall/custom.rs index e21fa127..08253f14 100644 --- a/kernel/src/syscall/custom.rs +++ b/kernel/src/syscall/custom.rs @@ -3,53 +3,56 @@ use super::*; use rcore_memory::memory_set::handler::Linear; use rcore_memory::memory_set::MemoryAttr; -/// Allocate this PCI device to user space -/// The kernel driver using the PCI device will be unloaded -#[cfg(target_arch = "x86_64")] -pub fn sys_map_pci_device(vendor: usize, product: usize) -> SysResult { - use crate::drivers::bus::pci; - info!( - "map_pci_device: vendor: {:x}, product: {:x}", - vendor, product - ); +impl Syscall<'_> { + /// Allocate this PCI device to user space + /// The kernel driver using the PCI device will be unloaded + #[cfg(target_arch = "x86_64")] + pub fn sys_map_pci_device(&mut self, vendor: usize, product: usize) -> SysResult { + use crate::drivers::bus::pci; + info!( + "map_pci_device: vendor: {:x}, product: {:x}", + vendor, product + ); - let tag = pci::find_device(vendor as u16, product as u16).ok_or(SysError::ENOENT)?; - if pci::detach_driver(&tag) { - info!("Kernel driver detached"); + let tag = pci::find_device(vendor as u16, product as u16).ok_or(SysError::ENOENT)?; + if pci::detach_driver(&tag) { + info!("Kernel driver detached"); + } + + // Get BAR0 memory + let (base, len) = pci::get_bar0_mem(tag).ok_or(SysError::ENOENT)?; + + let virt_addr = self.vm().find_free_area(0, len); + let attr = MemoryAttr::default().user(); + self.vm().push( + virt_addr, + virt_addr + len, + attr, + Linear::new(base as isize - virt_addr as isize), + "pci", + ); + Ok(virt_addr) } - // Get BAR0 memory - let (base, len) = pci::get_bar0_mem(tag).ok_or(SysError::ENOENT)?; - - let mut proc = process(); - let virt_addr = proc.vm.find_free_area(0, len); - let attr = MemoryAttr::default().user(); - proc.vm.push( - virt_addr, - virt_addr + len, - attr, - Linear::new(base as isize - virt_addr as isize), - "pci", - ); - Ok(virt_addr) -} - -#[cfg(not(target_arch = "x86_64"))] -pub fn sys_map_pci_device(vendor: usize, product: usize) -> SysResult { - Err(SysError::ENOSYS) -} - -/// Get start physical addresses of frames -/// mapped to a list of virtual addresses. -pub fn sys_get_paddr(vaddrs: *const u64, paddrs: *mut u64, count: usize) -> SysResult { - let mut proc = process(); - proc.vm.check_read_array(vaddrs, count)?; - proc.vm.check_write_array(paddrs, count)?; - let vaddrs = unsafe { slice::from_raw_parts(vaddrs, count) }; - let paddrs = unsafe { slice::from_raw_parts_mut(paddrs, count) }; - for i in 0..count { - let paddr = proc.vm.translate(vaddrs[i] as usize).unwrap_or(0); - paddrs[i] = paddr as u64; + #[cfg(not(target_arch = "x86_64"))] + pub fn sys_map_pci_device(&mut self, vendor: usize, product: usize) -> SysResult { + Err(SysError::ENOSYS) + } + + /// Get start physical addresses of frames + /// mapped to a list of virtual addresses. + pub fn sys_get_paddr( + &mut self, + vaddrs: *const u64, + paddrs: *mut u64, + count: usize, + ) -> SysResult { + let vaddrs = unsafe { self.vm().check_read_array(vaddrs, count)? }; + let paddrs = unsafe { self.vm().check_write_array(paddrs, count)? }; + for i in 0..count { + let paddr = self.vm().translate(vaddrs[i] as usize).unwrap_or(0); + paddrs[i] = paddr as u64; + } + Ok(0) } - Ok(0) } diff --git a/kernel/src/syscall/fs.rs b/kernel/src/syscall/fs.rs index b6075381..26d3f786 100644 --- a/kernel/src/syscall/fs.rs +++ b/kernel/src/syscall/fs.rs @@ -15,722 +15,787 @@ use bitvec::prelude::{BitSlice, BitVec, LittleEndian}; use super::*; -pub fn sys_read(fd: usize, base: *mut u8, len: usize) -> SysResult { - let mut proc = process(); - if !proc.pid.is_init() { - // we trust pid 0 process - info!("read: fd: {}, base: {:?}, len: {:#x}", fd, base, len); +impl Syscall<'_> { + pub fn sys_read(&mut self, fd: usize, base: *mut u8, len: usize) -> SysResult { + let mut proc = self.process(); + if !proc.pid.is_init() { + // we trust pid 0 process + info!("read: fd: {}, base: {:?}, len: {:#x}", fd, base, len); + } + let slice = unsafe { self.vm().check_write_array(base, len)? }; + let file_like = proc.get_file_like(fd)?; + let len = file_like.read(slice)?; + Ok(len) } - proc.vm.check_write_array(base, len)?; - let slice = unsafe { slice::from_raw_parts_mut(base, len) }; - let file_like = proc.get_file_like(fd)?; - let len = file_like.read(slice)?; - Ok(len) -} -pub fn sys_write(fd: usize, base: *const u8, len: usize) -> SysResult { - let mut proc = process(); - if !proc.pid.is_init() { - // we trust pid 0 process - info!("write: fd: {}, base: {:?}, len: {:#x}", fd, base, len); + pub fn sys_write(&mut self, fd: usize, base: *const u8, len: usize) -> SysResult { + let mut proc = self.process(); + if !proc.pid.is_init() { + // we trust pid 0 process + info!("write: fd: {}, base: {:?}, len: {:#x}", fd, base, len); + } + let slice = unsafe { self.vm().check_read_array(base, len)? }; + let file_like = proc.get_file_like(fd)?; + let len = file_like.write(slice)?; + Ok(len) } - proc.vm.check_read_array(base, len)?; - let slice = unsafe { slice::from_raw_parts(base, len) }; - let file_like = proc.get_file_like(fd)?; - let len = file_like.write(slice)?; - Ok(len) -} -pub fn sys_pread(fd: usize, base: *mut u8, len: usize, offset: usize) -> SysResult { - info!( - "pread: fd: {}, base: {:?}, len: {}, offset: {}", - fd, base, len, offset - ); - let mut proc = process(); - proc.vm.check_write_array(base, len)?; - - let slice = unsafe { slice::from_raw_parts_mut(base, len) }; - let len = proc.get_file(fd)?.read_at(offset, slice)?; - Ok(len) -} - -pub fn sys_pwrite(fd: usize, base: *const u8, len: usize, offset: usize) -> SysResult { - info!( - "pwrite: fd: {}, base: {:?}, len: {}, offset: {}", - fd, base, len, offset - ); - let mut proc = process(); - proc.vm.check_read_array(base, len)?; - - let slice = unsafe { slice::from_raw_parts(base, len) }; - let len = proc.get_file(fd)?.write_at(offset, slice)?; - Ok(len) -} - -pub fn sys_ppoll(ufds: *mut PollFd, nfds: usize, timeout: *const TimeSpec) -> SysResult { - let proc = process(); - let timeout_msecs = if timeout.is_null() { - 1 << 31 // infinity - } else { - proc.vm.check_read_ptr(timeout)?; - unsafe { (*timeout).to_msec() } - }; - drop(proc); - - sys_poll(ufds, nfds, timeout_msecs as usize) -} - -pub fn sys_poll(ufds: *mut PollFd, nfds: usize, timeout_msecs: usize) -> SysResult { - let proc = process(); - if !proc.pid.is_init() { - // we trust pid 0 process + pub fn sys_pread(&mut self, fd: usize, base: *mut u8, len: usize, offset: usize) -> SysResult { info!( - "poll: ufds: {:?}, nfds: {}, timeout_msecs: {:#x}", - ufds, nfds, timeout_msecs + "pread: fd: {}, base: {:?}, len: {}, offset: {}", + fd, base, len, offset ); + let mut proc = self.process(); + let slice = unsafe { self.vm().check_write_array(base, len)? }; + let len = proc.get_file(fd)?.read_at(offset, slice)?; + Ok(len) } - proc.vm.check_write_array(ufds, nfds)?; - let polls = unsafe { slice::from_raw_parts_mut(ufds, nfds) }; - for poll in polls.iter() { - if proc.files.get(&(poll.fd as usize)).is_none() { - return Err(SysError::EINVAL); - } - } - drop(proc); - - let begin_time_ms = crate::trap::uptime_msec(); - loop { - use PollEvents as PE; - let proc = process(); - let mut events = 0; - for poll in polls.iter_mut() { - poll.revents = PE::empty(); - if let Some(file_like) = proc.files.get(&(poll.fd as usize)) { - let status = file_like.poll()?; - if status.error { - poll.revents |= PE::HUP; - events += 1; - } - if status.read && poll.events.contains(PE::IN) { - poll.revents |= PE::IN; - events += 1; - } - if status.write && poll.events.contains(PE::OUT) { - poll.revents |= PE::OUT; - events += 1; - } - } else { - poll.revents |= PE::ERR; - events += 1; - } - } - drop(proc); - - if events > 0 { - return Ok(events); - } - - let current_time_ms = crate::trap::uptime_msec(); - if timeout_msecs < (1 << 31) && current_time_ms - begin_time_ms > timeout_msecs { - return Ok(0); - } - - Condvar::wait_any(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)]); - } -} - -pub fn sys_select( - nfds: usize, - read: *mut u32, - write: *mut u32, - err: *mut u32, - timeout: *const TimeVal, -) -> SysResult { - info!( - "select: nfds: {}, read: {:?}, write: {:?}, err: {:?}, timeout: {:?}", - nfds, read, write, err, timeout - ); - - let proc = process(); - let mut read_fds = FdSet::new(&proc.vm, read, nfds)?; - let mut write_fds = FdSet::new(&proc.vm, write, nfds)?; - let mut err_fds = FdSet::new(&proc.vm, err, nfds)?; - let timeout_msecs = if timeout as usize != 0 { - proc.vm.check_read_ptr(timeout)?; - unsafe { *timeout }.to_msec() - } else { - // infinity - 1 << 31 - }; - drop(proc); - - let begin_time_ms = crate::trap::uptime_msec(); - loop { - let proc = process(); - let mut events = 0; - for (&fd, file_like) in proc.files.iter() { - if fd >= nfds { - continue; - } - let status = file_like.poll()?; - if status.error && err_fds.contains(fd) { - err_fds.set(fd); - events += 1; - } - if status.read && read_fds.contains(fd) { - read_fds.set(fd); - events += 1; - } - if status.write && write_fds.contains(fd) { - write_fds.set(fd); - events += 1; - } - } - drop(proc); - - if events > 0 { - return Ok(events); - } - - if timeout_msecs == 0 { - // no timeout, return now; - return Ok(0); - } - - let current_time_ms = crate::trap::uptime_msec(); - // infinity check - if timeout_msecs < (1 << 31) && current_time_ms - begin_time_ms > timeout_msecs as usize { - return Ok(0); - } - - Condvar::wait_any(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)]); - } -} - -pub fn sys_readv(fd: usize, iov_ptr: *const IoVec, iov_count: usize) -> SysResult { - info!( - "readv: fd: {}, iov: {:?}, count: {}", - fd, iov_ptr, iov_count - ); - let mut proc = process(); - let mut iovs = IoVecs::check_and_new(iov_ptr, iov_count, &proc.vm, true)?; - - // read all data to a buf - let file_like = proc.get_file_like(fd)?; - let mut buf = iovs.new_buf(true); - let len = file_like.read(buf.as_mut_slice())?; - // copy data to user - iovs.write_all_from_slice(&buf[..len]); - Ok(len) -} - -pub fn sys_writev(fd: usize, iov_ptr: *const IoVec, iov_count: usize) -> SysResult { - let mut proc = process(); - if !proc.pid.is_init() { - // we trust pid 0 process + pub fn sys_pwrite( + &mut self, + fd: usize, + base: *const u8, + len: usize, + offset: usize, + ) -> SysResult { info!( - "writev: fd: {}, iov: {:?}, count: {}", + "pwrite: fd: {}, base: {:?}, len: {}, offset: {}", + fd, base, len, offset + ); + let mut proc = self.process(); + let slice = unsafe { self.vm().check_read_array(base, len)? }; + let len = proc.get_file(fd)?.write_at(offset, slice)?; + Ok(len) + } + + pub fn sys_ppoll( + &mut self, + ufds: *mut PollFd, + nfds: usize, + timeout: *const TimeSpec, + ) -> SysResult { + let proc = self.process(); + let timeout_msecs = if timeout.is_null() { + 1 << 31 // infinity + } else { + let timeout = unsafe { self.vm().check_read_ptr(timeout)? }; + timeout.to_msec() + }; + drop(proc); + + self.sys_poll(ufds, nfds, timeout_msecs as usize) + } + + pub fn sys_poll(&mut self, ufds: *mut PollFd, nfds: usize, timeout_msecs: usize) -> SysResult { + let proc = self.process(); + if !proc.pid.is_init() { + // we trust pid 0 process + info!( + "poll: ufds: {:?}, nfds: {}, timeout_msecs: {:#x}", + ufds, nfds, timeout_msecs + ); + } + + let polls = unsafe { self.vm().check_write_array(ufds, nfds)? }; + for poll in polls.iter() { + if proc.files.get(&(poll.fd as usize)).is_none() { + return Err(SysError::EINVAL); + } + } + drop(proc); + + let begin_time_ms = crate::trap::uptime_msec(); + loop { + use PollEvents as PE; + let proc = self.process(); + let mut events = 0; + for poll in polls.iter_mut() { + poll.revents = PE::empty(); + if let Some(file_like) = proc.files.get(&(poll.fd as usize)) { + let status = file_like.poll()?; + if status.error { + poll.revents |= PE::HUP; + events += 1; + } + if status.read && poll.events.contains(PE::IN) { + poll.revents |= PE::IN; + events += 1; + } + if status.write && poll.events.contains(PE::OUT) { + poll.revents |= PE::OUT; + events += 1; + } + } else { + poll.revents |= PE::ERR; + events += 1; + } + } + drop(proc); + + if events > 0 { + return Ok(events); + } + + let current_time_ms = crate::trap::uptime_msec(); + if timeout_msecs < (1 << 31) && current_time_ms - begin_time_ms > timeout_msecs { + return Ok(0); + } + + Condvar::wait_any(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)]); + } + } + + pub fn sys_select( + &mut self, + nfds: usize, + read: *mut u32, + write: *mut u32, + err: *mut u32, + timeout: *const TimeVal, + ) -> SysResult { + info!( + "select: nfds: {}, read: {:?}, write: {:?}, err: {:?}, timeout: {:?}", + nfds, read, write, err, timeout + ); + + let proc = self.process(); + let mut read_fds = FdSet::new(&self.vm(), read, nfds)?; + let mut write_fds = FdSet::new(&self.vm(), write, nfds)?; + let mut err_fds = FdSet::new(&self.vm(), err, nfds)?; + let timeout_msecs = if !timeout.is_null() { + let timeout = unsafe { self.vm().check_read_ptr(timeout)? }; + timeout.to_msec() + } else { + // infinity + 1 << 31 + }; + + // for debugging + if cfg!(debug_assertions) { + debug!("files before select {:#?}", proc.files); + } + drop(proc); + + let begin_time_ms = crate::trap::uptime_msec(); + loop { + let proc = self.process(); + let mut events = 0; + for (&fd, file_like) in proc.files.iter() { + if fd >= nfds { + continue; + } + if !err_fds.contains(fd) && !read_fds.contains(fd) && !write_fds.contains(fd) { + continue; + } + let status = file_like.poll()?; + if status.error && err_fds.contains(fd) { + err_fds.set(fd); + events += 1; + } + if status.read && read_fds.contains(fd) { + read_fds.set(fd); + events += 1; + } + if status.write && write_fds.contains(fd) { + write_fds.set(fd); + events += 1; + } + } + drop(proc); + + if events > 0 { + return Ok(events); + } + + if timeout_msecs == 0 { + // no timeout, return now; + return Ok(0); + } + + let current_time_ms = crate::trap::uptime_msec(); + // infinity check + if timeout_msecs < (1 << 31) && current_time_ms - begin_time_ms > timeout_msecs as usize + { + return Ok(0); + } + + Condvar::wait_any(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)]); + } + } + + pub fn sys_readv(&mut self, fd: usize, iov_ptr: *const IoVec, iov_count: usize) -> SysResult { + info!( + "readv: fd: {}, iov: {:?}, count: {}", fd, iov_ptr, iov_count ); - } - let iovs = IoVecs::check_and_new(iov_ptr, iov_count, &proc.vm, false)?; + let mut proc = self.process(); + let mut iovs = unsafe { IoVecs::check_and_new(iov_ptr, iov_count, &self.vm(), true)? }; - let buf = iovs.read_all_to_vec(); - let len = buf.len(); - - let file_like = proc.get_file_like(fd)?; - let len = file_like.write(buf.as_slice())?; - Ok(len) -} - -pub fn sys_open(path: *const u8, flags: usize, mode: usize) -> SysResult { - sys_openat(AT_FDCWD, path, flags, mode) -} - -pub fn sys_openat(dir_fd: usize, path: *const u8, flags: usize, mode: usize) -> SysResult { - let mut proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - let flags = OpenFlags::from_bits_truncate(flags); - info!( - "openat: dir_fd: {}, path: {:?}, flags: {:?}, mode: {:#o}", - dir_fd as isize, path, flags, mode - ); - - let inode = if flags.contains(OpenFlags::CREATE) { - let (dir_path, file_name) = split_path(&path); - // relative to cwd - let dir_inode = proc.lookup_inode_at(dir_fd, dir_path, true)?; - match dir_inode.find(file_name) { - Ok(file_inode) => { - if flags.contains(OpenFlags::EXCLUSIVE) { - return Err(SysError::EEXIST); - } - file_inode - } - Err(FsError::EntryNotFound) => { - dir_inode.create(file_name, FileType::File, mode as u32)? - } - Err(e) => return Err(SysError::from(e)), - } - } else { - proc.lookup_inode_at(dir_fd, &path, true)? - }; - - let fd = proc.get_free_fd(); - - let file = FileHandle::new(inode, flags.to_options()); - proc.files.insert(fd, FileLike::File(file)); - Ok(fd) -} - -pub fn sys_close(fd: usize) -> SysResult { - info!("close: fd: {:?}", fd); - let mut proc = process(); - proc.files.remove(&fd).ok_or(SysError::EBADF)?; - Ok(0) -} - -pub fn sys_access(path: *const u8, mode: usize) -> SysResult { - sys_faccessat(AT_FDCWD, path, mode, 0) -} - -pub fn sys_faccessat(dirfd: usize, path: *const u8, mode: usize, flags: usize) -> SysResult { - // TODO: check permissions based on uid/gid - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - let flags = AtFlags::from_bits_truncate(flags); - if !proc.pid.is_init() { - // we trust pid 0 process - info!( - "faccessat: dirfd: {}, path: {:?}, mode: {:#o}, flags: {:?}", - dirfd as isize, path, mode, flags - ); - } - let inode = proc.lookup_inode_at(dirfd, &path, !flags.contains(AtFlags::SYMLINK_NOFOLLOW))?; - Ok(0) -} - -pub fn sys_getcwd(buf: *mut u8, len: usize) -> SysResult { - let proc = process(); - if !proc.pid.is_init() { - // we trust pid 0 process - info!("getcwd: buf: {:?}, len: {:#x}", buf, len); - } - proc.vm.check_write_array(buf, len)?; - if proc.cwd.len() + 1 > len { - return Err(SysError::ERANGE); - } - unsafe { util::write_cstr(buf, &proc.cwd) } - Ok(buf as usize) -} - -pub fn sys_lstat(path: *const u8, stat_ptr: *mut Stat) -> SysResult { - sys_fstatat(AT_FDCWD, path, stat_ptr, AtFlags::SYMLINK_NOFOLLOW.bits()) -} - -pub fn sys_fstat(fd: usize, stat_ptr: *mut Stat) -> SysResult { - info!("fstat: fd: {}, stat_ptr: {:?}", fd, stat_ptr); - let mut proc = process(); - proc.vm.check_write_ptr(stat_ptr)?; - let file = proc.get_file(fd)?; - let stat = Stat::from(file.metadata()?); - unsafe { - stat_ptr.write(stat); - } - Ok(0) -} - -pub fn sys_fstatat(dirfd: usize, path: *const u8, stat_ptr: *mut Stat, flags: usize) -> SysResult { - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - proc.vm.check_write_ptr(stat_ptr)?; - let flags = AtFlags::from_bits_truncate(flags); - info!( - "fstatat: dirfd: {}, path: {:?}, stat_ptr: {:?}, flags: {:?}", - dirfd as isize, path, stat_ptr, flags - ); - - let inode = proc.lookup_inode_at(dirfd, &path, !flags.contains(AtFlags::SYMLINK_NOFOLLOW))?; - let stat = Stat::from(inode.metadata()?); - unsafe { - stat_ptr.write(stat); - } - Ok(0) -} - -pub fn sys_stat(path: *const u8, stat_ptr: *mut Stat) -> SysResult { - sys_fstatat(AT_FDCWD, path, stat_ptr, 0) -} - -pub fn sys_readlink(path: *const u8, base: *mut u8, len: usize) -> SysResult { - sys_readlinkat(AT_FDCWD, path, base, len) -} - -pub fn sys_readlinkat(dirfd: usize, path: *const u8, base: *mut u8, len: usize) -> SysResult { - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - proc.vm.check_write_array(base, len)?; - info!("readlink: path: {:?}, base: {:?}, len: {}", path, base, len); - - let inode = proc.lookup_inode_at(dirfd, &path, false)?; - if inode.metadata()?.type_ == FileType::SymLink { - // TODO: recursive link resolution and loop detection - let mut slice = unsafe { slice::from_raw_parts_mut(base, len) }; - let len = inode.read_at(0, &mut slice)?; + // read all data to a buf + let file_like = proc.get_file_like(fd)?; + let mut buf = iovs.new_buf(true); + let len = file_like.read(buf.as_mut_slice())?; + // copy data to user + iovs.write_all_from_slice(&buf[..len]); Ok(len) - } else { - Err(SysError::EINVAL) } -} -pub fn sys_lseek(fd: usize, offset: i64, whence: u8) -> SysResult { - let pos = match whence { - SEEK_SET => SeekFrom::Start(offset as u64), - SEEK_END => SeekFrom::End(offset), - SEEK_CUR => SeekFrom::Current(offset), - _ => return Err(SysError::EINVAL), - }; - info!("lseek: fd: {}, pos: {:?}", fd, pos); + pub fn sys_writev(&mut self, fd: usize, iov_ptr: *const IoVec, iov_count: usize) -> SysResult { + let mut proc = self.process(); + if !proc.pid.is_init() { + // we trust pid 0 process + info!( + "writev: fd: {}, iov: {:?}, count: {}", + fd, iov_ptr, iov_count + ); + } + let iovs = unsafe { IoVecs::check_and_new(iov_ptr, iov_count, &self.vm(), false)? }; - let mut proc = process(); - let file = proc.get_file(fd)?; - let offset = file.seek(pos)?; - Ok(offset as usize) -} - -pub fn sys_fsync(fd: usize) -> SysResult { - info!("fsync: fd: {}", fd); - process().get_file(fd)?.sync_all()?; - Ok(0) -} - -pub fn sys_fdatasync(fd: usize) -> SysResult { - info!("fdatasync: fd: {}", fd); - process().get_file(fd)?.sync_data()?; - Ok(0) -} - -pub fn sys_truncate(path: *const u8, len: usize) -> SysResult { - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - info!("truncate: path: {:?}, len: {}", path, len); - proc.lookup_inode(&path)?.resize(len)?; - Ok(0) -} - -pub fn sys_ftruncate(fd: usize, len: usize) -> SysResult { - info!("ftruncate: fd: {}, len: {}", fd, len); - process().get_file(fd)?.set_len(len as u64)?; - Ok(0) -} - -pub fn sys_getdents64(fd: usize, buf: *mut LinuxDirent64, buf_size: usize) -> SysResult { - info!( - "getdents64: fd: {}, ptr: {:?}, buf_size: {}", - fd, buf, buf_size - ); - let mut proc = process(); - proc.vm.check_write_array(buf as *mut u8, buf_size)?; - let file = proc.get_file(fd)?; - let info = file.metadata()?; - if info.type_ != FileType::Dir { - return Err(SysError::ENOTDIR); + let buf = iovs.read_all_to_vec(); + let file_like = proc.get_file_like(fd)?; + let len = file_like.write(buf.as_slice())?; + Ok(len) } - let mut writer = unsafe { DirentBufWriter::new(buf, buf_size) }; - loop { - let name = match file.read_entry() { - Err(FsError::EntryNotFound) => break, - r => r, - }?; - // TODO: get ino from dirent - let ok = writer.try_write(0, DirentType::from_type(&info.type_).bits(), &name); - if !ok { - break; + + pub fn sys_open(&mut self, path: *const u8, flags: usize, mode: usize) -> SysResult { + self.sys_openat(AT_FDCWD, path, flags, mode) + } + + pub fn sys_openat( + &mut self, + dir_fd: usize, + path: *const u8, + flags: usize, + mode: usize, + ) -> SysResult { + let mut proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + let flags = OpenFlags::from_bits_truncate(flags); + info!( + "openat: dir_fd: {}, path: {:?}, flags: {:?}, mode: {:#o}", + dir_fd as isize, path, flags, mode + ); + + let inode = if flags.contains(OpenFlags::CREATE) { + let (dir_path, file_name) = split_path(&path); + // relative to cwd + let dir_inode = proc.lookup_inode_at(dir_fd, dir_path, true)?; + match dir_inode.find(file_name) { + Ok(file_inode) => { + if flags.contains(OpenFlags::EXCLUSIVE) { + return Err(SysError::EEXIST); + } + file_inode + } + Err(FsError::EntryNotFound) => { + dir_inode.create(file_name, FileType::File, mode as u32)? + } + Err(e) => return Err(SysError::from(e)), + } + } else { + proc.lookup_inode_at(dir_fd, &path, true)? + }; + + let file = FileHandle::new(inode, flags.to_options(), String::from(path)); + + // for debugging + if cfg!(debug_assertions) { + debug!("files before open {:#?}", proc.files); + } + + let fd = proc.add_file(FileLike::File(file)); + Ok(fd) + } + + pub fn sys_close(&mut self, fd: usize) -> SysResult { + info!("close: fd: {:?}", fd); + let mut proc = self.process(); + + // for debugging + if cfg!(debug_assertions) { + debug!("files before close {:#?}", proc.files); + } + + proc.files.remove(&fd).ok_or(SysError::EBADF)?; + Ok(0) + } + + pub fn sys_access(&mut self, path: *const u8, mode: usize) -> SysResult { + self.sys_faccessat(AT_FDCWD, path, mode, 0) + } + + pub fn sys_faccessat( + &mut self, + dirfd: usize, + path: *const u8, + mode: usize, + flags: usize, + ) -> SysResult { + // TODO: check permissions based on uid/gid + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + let flags = AtFlags::from_bits_truncate(flags); + if !proc.pid.is_init() { + // we trust pid 0 process + info!( + "faccessat: dirfd: {}, path: {:?}, mode: {:#o}, flags: {:?}", + dirfd as isize, path, mode, flags + ); + } + let inode = + proc.lookup_inode_at(dirfd, &path, !flags.contains(AtFlags::SYMLINK_NOFOLLOW))?; + Ok(0) + } + + pub fn sys_getcwd(&mut self, buf: *mut u8, len: usize) -> SysResult { + let proc = self.process(); + if !proc.pid.is_init() { + // we trust pid 0 process + info!("getcwd: buf: {:?}, len: {:#x}", buf, len); + } + let buf = unsafe { self.vm().check_write_array(buf, len)? }; + if proc.cwd.len() + 1 > len { + return Err(SysError::ERANGE); + } + unsafe { util::write_cstr(buf.as_mut_ptr(), &proc.cwd) } + Ok(buf.as_ptr() as usize) + } + + pub fn sys_lstat(&mut self, path: *const u8, stat_ptr: *mut Stat) -> SysResult { + self.sys_fstatat(AT_FDCWD, path, stat_ptr, AtFlags::SYMLINK_NOFOLLOW.bits()) + } + + pub fn sys_fstat(&mut self, fd: usize, stat_ptr: *mut Stat) -> SysResult { + info!("fstat: fd: {}, stat_ptr: {:?}", fd, stat_ptr); + let mut proc = self.process(); + let stat_ref = unsafe { self.vm().check_write_ptr(stat_ptr)? }; + let file = proc.get_file(fd)?; + let stat = Stat::from(file.metadata()?); + *stat_ref = stat; + Ok(0) + } + + pub fn sys_fstatat( + &mut self, + dirfd: usize, + path: *const u8, + stat_ptr: *mut Stat, + flags: usize, + ) -> SysResult { + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + let stat_ref = unsafe { self.vm().check_write_ptr(stat_ptr)? }; + let flags = AtFlags::from_bits_truncate(flags); + info!( + "fstatat: dirfd: {}, path: {:?}, stat_ptr: {:?}, flags: {:?}", + dirfd as isize, path, stat_ptr, flags + ); + + let inode = + proc.lookup_inode_at(dirfd, &path, !flags.contains(AtFlags::SYMLINK_NOFOLLOW))?; + let stat = Stat::from(inode.metadata()?); + *stat_ref = stat; + Ok(0) + } + + pub fn sys_stat(&mut self, path: *const u8, stat_ptr: *mut Stat) -> SysResult { + self.sys_fstatat(AT_FDCWD, path, stat_ptr, 0) + } + + pub fn sys_readlink(&mut self, path: *const u8, base: *mut u8, len: usize) -> SysResult { + self.sys_readlinkat(AT_FDCWD, path, base, len) + } + + pub fn sys_readlinkat( + &mut self, + dirfd: usize, + path: *const u8, + base: *mut u8, + len: usize, + ) -> SysResult { + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + let slice = unsafe { self.vm().check_write_array(base, len)? }; + info!( + "readlinkat: dirfd: {}, path: {:?}, base: {:?}, len: {}", + dirfd as isize, path, base, len + ); + + let inode = proc.lookup_inode_at(dirfd, &path, false)?; + if inode.metadata()?.type_ == FileType::SymLink { + // TODO: recursive link resolution and loop detection + let len = inode.read_at(0, slice)?; + Ok(len) + } else { + Err(SysError::EINVAL) } } - Ok(writer.written_size) -} -pub fn sys_dup2(fd1: usize, fd2: usize) -> SysResult { - info!("dup2: from {} to {}", fd1, fd2); - let mut proc = process(); - // close fd2 first if it is opened - proc.files.remove(&fd2); - - let file_like = proc.get_file_like(fd1)?.clone(); - proc.files.insert(fd2, file_like); - Ok(fd2) -} - -pub fn sys_ioctl(fd: usize, request: usize, arg1: usize, arg2: usize, arg3: usize) -> SysResult { - info!( - "ioctl: fd: {}, request: {:x}, args: {} {} {}", - fd, request, arg1, arg2, arg3 - ); - let mut proc = process(); - let file_like = proc.get_file_like(fd)?; - file_like.ioctl(request, arg1, arg2, arg3) -} - -pub fn sys_chdir(path: *const u8) -> SysResult { - let mut proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - if !proc.pid.is_init() { - // we trust pid 0 process - info!("chdir: path: {:?}", path); - } - - let inode = proc.lookup_inode(&path)?; - let info = inode.metadata()?; - if info.type_ != FileType::Dir { - return Err(SysError::ENOTDIR); - } - - // BUGFIX: '..' and '.' - if path.len() > 0 { - let cwd = match path.as_bytes()[0] { - b'/' => String::from("/"), - _ => proc.cwd.clone(), + pub fn sys_lseek(&mut self, fd: usize, offset: i64, whence: u8) -> SysResult { + let pos = match whence { + SEEK_SET => SeekFrom::Start(offset as u64), + SEEK_END => SeekFrom::End(offset), + SEEK_CUR => SeekFrom::Current(offset), + _ => return Err(SysError::EINVAL), }; - let mut cwd_vec: Vec<_> = cwd.split("/").filter(|&x| x != "").collect(); - let path_split = path.split("/").filter(|&x| x != ""); - for seg in path_split { - if seg == ".." { - cwd_vec.pop(); - } else if seg == "." { - // nothing to do here. - } else { - cwd_vec.push(seg); + info!("lseek: fd: {}, pos: {:?}", fd, pos); + + let mut proc = self.process(); + let file = proc.get_file(fd)?; + let offset = file.seek(pos)?; + Ok(offset as usize) + } + + pub fn sys_fsync(&mut self, fd: usize) -> SysResult { + info!("fsync: fd: {}", fd); + self.process().get_file(fd)?.sync_all()?; + Ok(0) + } + + pub fn sys_fdatasync(&mut self, fd: usize) -> SysResult { + info!("fdatasync: fd: {}", fd); + self.process().get_file(fd)?.sync_data()?; + Ok(0) + } + + pub fn sys_truncate(&mut self, path: *const u8, len: usize) -> SysResult { + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + info!("truncate: path: {:?}, len: {}", path, len); + proc.lookup_inode(&path)?.resize(len)?; + Ok(0) + } + + pub fn sys_ftruncate(&mut self, fd: usize, len: usize) -> SysResult { + info!("ftruncate: fd: {}, len: {}", fd, len); + self.process().get_file(fd)?.set_len(len as u64)?; + Ok(0) + } + + pub fn sys_getdents64( + &mut self, + fd: usize, + buf: *mut LinuxDirent64, + buf_size: usize, + ) -> SysResult { + info!( + "getdents64: fd: {}, ptr: {:?}, buf_size: {}", + fd, buf, buf_size + ); + let mut proc = self.process(); + let buf = unsafe { self.vm().check_write_array(buf as *mut u8, buf_size)? }; + let file = proc.get_file(fd)?; + let info = file.metadata()?; + if info.type_ != FileType::Dir { + return Err(SysError::ENOTDIR); + } + let mut writer = DirentBufWriter::new(buf); + loop { + let name = match file.read_entry() { + Err(FsError::EntryNotFound) => break, + r => r, + }?; + // TODO: get ino from dirent + let ok = writer.try_write(0, DirentType::from_type(&info.type_).bits(), &name); + if !ok { + break; } } - proc.cwd = String::from(""); - for seg in cwd_vec { - proc.cwd.push_str("/"); - proc.cwd.push_str(seg); + Ok(writer.written_size) + } + + pub fn sys_dup2(&mut self, fd1: usize, fd2: usize) -> SysResult { + info!("dup2: from {} to {}", fd1, fd2); + let mut proc = self.process(); + // close fd2 first if it is opened + proc.files.remove(&fd2); + + let file_like = proc.get_file_like(fd1)?.clone(); + proc.files.insert(fd2, file_like); + Ok(fd2) + } + + pub fn sys_ioctl( + &mut self, + fd: usize, + request: usize, + arg1: usize, + arg2: usize, + arg3: usize, + ) -> SysResult { + info!( + "ioctl: fd: {}, request: {:x}, args: {} {} {}", + fd, request, arg1, arg2, arg3 + ); + let mut proc = self.process(); + let file_like = proc.get_file_like(fd)?; + file_like.ioctl(request, arg1, arg2, arg3) + } + + pub fn sys_chdir(&mut self, path: *const u8) -> SysResult { + let mut proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + if !proc.pid.is_init() { + // we trust pid 0 process + info!("chdir: path: {:?}", path); } - if proc.cwd == "" { - proc.cwd = String::from("/"); + + let inode = proc.lookup_inode(&path)?; + let info = inode.metadata()?; + if info.type_ != FileType::Dir { + return Err(SysError::ENOTDIR); } + + // BUGFIX: '..' and '.' + if path.len() > 0 { + let cwd = match path.as_bytes()[0] { + b'/' => String::from("/"), + _ => proc.cwd.clone(), + }; + let mut cwd_vec: Vec<_> = cwd.split("/").filter(|&x| x != "").collect(); + let path_split = path.split("/").filter(|&x| x != ""); + for seg in path_split { + if seg == ".." { + cwd_vec.pop(); + } else if seg == "." { + // nothing to do here. + } else { + cwd_vec.push(seg); + } + } + proc.cwd = String::from(""); + for seg in cwd_vec { + proc.cwd.push_str("/"); + proc.cwd.push_str(seg); + } + if proc.cwd == "" { + proc.cwd = String::from("/"); + } + } + Ok(0) } - Ok(0) -} -pub fn sys_rename(oldpath: *const u8, newpath: *const u8) -> SysResult { - sys_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath) -} - -pub fn sys_renameat( - olddirfd: usize, - oldpath: *const u8, - newdirfd: usize, - newpath: *const u8, -) -> SysResult { - let mut proc = process(); - let oldpath = unsafe { proc.vm.check_and_clone_cstr(oldpath)? }; - let newpath = unsafe { proc.vm.check_and_clone_cstr(newpath)? }; - info!( - "renameat: olddirfd: {}, oldpath: {:?}, newdirfd: {}, newpath: {:?}", - olddirfd as isize, oldpath, newdirfd as isize, newpath - ); - - let (old_dir_path, old_file_name) = split_path(&oldpath); - let (new_dir_path, new_file_name) = split_path(&newpath); - let old_dir_inode = proc.lookup_inode_at(olddirfd, old_dir_path, false)?; - let new_dir_inode = proc.lookup_inode_at(newdirfd, new_dir_path, false)?; - old_dir_inode.move_(old_file_name, &new_dir_inode, new_file_name)?; - Ok(0) -} - -pub fn sys_mkdir(path: *const u8, mode: usize) -> SysResult { - sys_mkdirat(AT_FDCWD, path, mode) -} - -pub fn sys_mkdirat(dirfd: usize, path: *const u8, mode: usize) -> SysResult { - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - // TODO: check pathname - info!( - "mkdirat: dirfd: {}, path: {:?}, mode: {:#o}", - dirfd as isize, path, mode - ); - - let (dir_path, file_name) = split_path(&path); - let inode = proc.lookup_inode_at(dirfd, dir_path, true)?; - if inode.find(file_name).is_ok() { - return Err(SysError::EEXIST); + pub fn sys_rename(&mut self, oldpath: *const u8, newpath: *const u8) -> SysResult { + self.sys_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath) } - inode.create(file_name, FileType::Dir, mode as u32)?; - Ok(0) -} -pub fn sys_rmdir(path: *const u8) -> SysResult { - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - info!("rmdir: path: {:?}", path); + pub fn sys_renameat( + &mut self, + olddirfd: usize, + oldpath: *const u8, + newdirfd: usize, + newpath: *const u8, + ) -> SysResult { + let proc = self.process(); + let oldpath = unsafe { self.vm().check_and_clone_cstr(oldpath)? }; + let newpath = unsafe { self.vm().check_and_clone_cstr(newpath)? }; + info!( + "renameat: olddirfd: {}, oldpath: {:?}, newdirfd: {}, newpath: {:?}", + olddirfd as isize, oldpath, newdirfd as isize, newpath + ); - let (dir_path, file_name) = split_path(&path); - let dir_inode = proc.lookup_inode(dir_path)?; - let file_inode = dir_inode.find(file_name)?; - if file_inode.metadata()?.type_ != FileType::Dir { - return Err(SysError::ENOTDIR); + let (old_dir_path, old_file_name) = split_path(&oldpath); + let (new_dir_path, new_file_name) = split_path(&newpath); + let old_dir_inode = proc.lookup_inode_at(olddirfd, old_dir_path, false)?; + let new_dir_inode = proc.lookup_inode_at(newdirfd, new_dir_path, false)?; + old_dir_inode.move_(old_file_name, &new_dir_inode, new_file_name)?; + Ok(0) } - dir_inode.unlink(file_name)?; - Ok(0) -} -pub fn sys_link(oldpath: *const u8, newpath: *const u8) -> SysResult { - sys_linkat(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0) -} - -pub fn sys_linkat( - olddirfd: usize, - oldpath: *const u8, - newdirfd: usize, - newpath: *const u8, - flags: usize, -) -> SysResult { - let proc = process(); - let oldpath = unsafe { proc.vm.check_and_clone_cstr(oldpath)? }; - let newpath = unsafe { proc.vm.check_and_clone_cstr(newpath)? }; - let flags = AtFlags::from_bits_truncate(flags); - info!( - "linkat: olddirfd: {}, oldpath: {:?}, newdirfd: {}, newpath: {:?}, flags: {:?}", - olddirfd as isize, oldpath, newdirfd as isize, newpath, flags - ); - - let (new_dir_path, new_file_name) = split_path(&newpath); - let inode = proc.lookup_inode_at(olddirfd, &oldpath, true)?; - let new_dir_inode = proc.lookup_inode_at(newdirfd, new_dir_path, true)?; - new_dir_inode.link(new_file_name, &inode)?; - Ok(0) -} - -pub fn sys_unlink(path: *const u8) -> SysResult { - sys_unlinkat(AT_FDCWD, path, 0) -} - -pub fn sys_unlinkat(dirfd: usize, path: *const u8, flags: usize) -> SysResult { - let proc = process(); - let path = unsafe { proc.vm.check_and_clone_cstr(path)? }; - let flags = AtFlags::from_bits_truncate(flags); - info!( - "unlinkat: dirfd: {}, path: {:?}, flags: {:?}", - dirfd as isize, path, flags - ); - - let (dir_path, file_name) = split_path(&path); - let dir_inode = proc.lookup_inode_at(dirfd, dir_path, true)?; - let file_inode = dir_inode.find(file_name)?; - if file_inode.metadata()?.type_ == FileType::Dir { - return Err(SysError::EISDIR); + pub fn sys_mkdir(&mut self, path: *const u8, mode: usize) -> SysResult { + self.sys_mkdirat(AT_FDCWD, path, mode) } - dir_inode.unlink(file_name)?; - Ok(0) -} -pub fn sys_pipe(fds: *mut u32) -> SysResult { - info!("pipe: fds: {:?}", fds); + pub fn sys_mkdirat(&mut self, dirfd: usize, path: *const u8, mode: usize) -> SysResult { + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + // TODO: check pathname + info!( + "mkdirat: dirfd: {}, path: {:?}, mode: {:#o}", + dirfd as isize, path, mode + ); - let mut proc = process(); - proc.vm.check_write_array(fds, 2)?; - let (read, write) = Pipe::create_pair(); + let (dir_path, file_name) = split_path(&path); + let inode = proc.lookup_inode_at(dirfd, dir_path, true)?; + if inode.find(file_name).is_ok() { + return Err(SysError::EEXIST); + } + inode.create(file_name, FileType::Dir, mode as u32)?; + Ok(0) + } - let read_fd = proc.get_free_fd(); - proc.files.insert( - read_fd, - FileLike::File(FileHandle::new( + pub fn sys_rmdir(&mut self, path: *const u8) -> SysResult { + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + info!("rmdir: path: {:?}", path); + + let (dir_path, file_name) = split_path(&path); + let dir_inode = proc.lookup_inode(dir_path)?; + let file_inode = dir_inode.find(file_name)?; + if file_inode.metadata()?.type_ != FileType::Dir { + return Err(SysError::ENOTDIR); + } + dir_inode.unlink(file_name)?; + Ok(0) + } + + pub fn sys_link(&mut self, oldpath: *const u8, newpath: *const u8) -> SysResult { + self.sys_linkat(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0) + } + + pub fn sys_linkat( + &mut self, + olddirfd: usize, + oldpath: *const u8, + newdirfd: usize, + newpath: *const u8, + flags: usize, + ) -> SysResult { + let proc = self.process(); + let oldpath = unsafe { self.vm().check_and_clone_cstr(oldpath)? }; + let newpath = unsafe { self.vm().check_and_clone_cstr(newpath)? }; + let flags = AtFlags::from_bits_truncate(flags); + info!( + "linkat: olddirfd: {}, oldpath: {:?}, newdirfd: {}, newpath: {:?}, flags: {:?}", + olddirfd as isize, oldpath, newdirfd as isize, newpath, flags + ); + + let (new_dir_path, new_file_name) = split_path(&newpath); + let inode = proc.lookup_inode_at(olddirfd, &oldpath, true)?; + let new_dir_inode = proc.lookup_inode_at(newdirfd, new_dir_path, true)?; + new_dir_inode.link(new_file_name, &inode)?; + Ok(0) + } + + pub fn sys_unlink(&mut self, path: *const u8) -> SysResult { + self.sys_unlinkat(AT_FDCWD, path, 0) + } + + pub fn sys_unlinkat(&mut self, dirfd: usize, path: *const u8, flags: usize) -> SysResult { + let proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + let flags = AtFlags::from_bits_truncate(flags); + info!( + "unlinkat: dirfd: {}, path: {:?}, flags: {:?}", + dirfd as isize, path, flags + ); + + let (dir_path, file_name) = split_path(&path); + let dir_inode = proc.lookup_inode_at(dirfd, dir_path, true)?; + let file_inode = dir_inode.find(file_name)?; + if file_inode.metadata()?.type_ == FileType::Dir { + return Err(SysError::EISDIR); + } + dir_inode.unlink(file_name)?; + Ok(0) + } + + pub fn sys_pipe(&mut self, fds: *mut u32) -> SysResult { + info!("pipe: fds: {:?}", fds); + + let mut proc = self.process(); + let fds = unsafe { self.vm().check_write_array(fds, 2)? }; + let (read, write) = Pipe::create_pair(); + + let read_fd = proc.add_file(FileLike::File(FileHandle::new( Arc::new(read), OpenOptions { read: true, write: false, append: false, }, - )), - ); + String::from("pipe_r:[]"), + ))); - let write_fd = proc.get_free_fd(); - proc.files.insert( - write_fd, - FileLike::File(FileHandle::new( + let write_fd = proc.add_file(FileLike::File(FileHandle::new( Arc::new(write), OpenOptions { read: false, write: true, append: false, }, - )), - ); + String::from("pipe_w:[]"), + ))); - unsafe { - fds.write(read_fd as u32); - fds.add(1).write(write_fd as u32); + fds[0] = read_fd as u32; + fds[1] = write_fd as u32; + + info!("pipe: created rfd: {} wfd: {}", read_fd, write_fd); + + Ok(0) } - info!("pipe: created rfd: {} wfd: {}", read_fd, write_fd); + pub fn sys_sync(&mut self) -> SysResult { + ROOT_INODE.fs().sync()?; + Ok(0) + } - Ok(0) -} + pub fn sys_sendfile( + &mut self, + out_fd: usize, + in_fd: usize, + offset_ptr: *mut usize, + count: usize, + ) -> SysResult { + info!( + "sendfile:BEG out: {}, in: {}, offset_ptr: {:?}, count: {}", + out_fd, in_fd, offset_ptr, count + ); + let proc = self.process(); + // We know it's save, pacify the borrow checker + let proc_cell = UnsafeCell::new(proc); + let in_file = unsafe { (*proc_cell.get()).get_file(in_fd)? }; + let out_file = unsafe { (*proc_cell.get()).get_file(out_fd)? }; + let mut buffer = [0u8; 1024]; -pub fn sys_sync() -> SysResult { - ROOT_INODE.fs().sync()?; - Ok(0) -} + let mut read_offset = if !offset_ptr.is_null() { + unsafe { *self.vm().check_read_ptr(offset_ptr)? } + } else { + in_file.seek(SeekFrom::Current(0))? as usize + }; -pub fn sys_sendfile( - out_fd: usize, - in_fd: usize, - offset_ptr: *mut usize, - count: usize, -) -> SysResult { - info!( - "sendfile: out: {}, in: {}, offset_ptr: {:?}, count: {}", - out_fd, in_fd, offset_ptr, count - ); - let proc = process(); - // We know it's save, pacify the borrow checker - let proc_cell = UnsafeCell::new(proc); - let in_file = unsafe { (*proc_cell.get()).get_file(in_fd)? }; - let out_file = unsafe { (*proc_cell.get()).get_file(out_fd)? }; - let mut buffer = [0u8; 1024]; - - let mut read_offset = if !offset_ptr.is_null() { - unsafe { - (*proc_cell.get()).vm.check_read_ptr(offset_ptr)?; - offset_ptr.read() - } - } else { - in_file.seek(SeekFrom::Current(0))? as usize - }; - - // read from specified offset and write new offset back - let mut bytes_read = 0; - while bytes_read < count { - let len = min(buffer.len(), count - bytes_read); - let read_len = in_file.read_at(read_offset, &mut buffer[..len])?; - if read_len == 0 { - break; - } - bytes_read += read_len; - read_offset += read_len; - let mut bytes_written = 0; - while bytes_written < read_len { - let write_len = out_file.write(&buffer[bytes_written..])?; - if write_len == 0 { - return Err(SysError::EBADF); + // read from specified offset and write new offset back + let mut bytes_read = 0; + let mut total_written = 0; + while bytes_read < count { + let len = min(buffer.len(), count - bytes_read); + let read_len = in_file.read_at(read_offset, &mut buffer[..len])?; + if read_len == 0 { + break; } - bytes_written += write_len; - } - } + bytes_read += read_len; + read_offset += read_len; - if !offset_ptr.is_null() { - unsafe { - offset_ptr.write(read_offset); + let mut bytes_written = 0; + let mut rlen = read_len; + while bytes_written < read_len { + let write_len = out_file.write(&buffer[bytes_written..(bytes_written + rlen)])?; + if write_len == 0 { + info!( + "sendfile:END_ERR out: {}, in: {}, offset_ptr: {:?}, count: {} = bytes_read {}, bytes_written {}, write_len {}", + out_fd, in_fd, offset_ptr, count, bytes_read, bytes_written, write_len + ); + return Err(SysError::EBADF); + } + bytes_written += write_len; + rlen -= write_len; + } + total_written += bytes_written; } - } else { - in_file.seek(SeekFrom::Current(bytes_read as i64))?; + + if !offset_ptr.is_null() { + unsafe { + offset_ptr.write(read_offset); + } + } else { + in_file.seek(SeekFrom::Current(bytes_read as i64))?; + } + info!( + "sendfile:END out: {}, in: {}, offset_ptr: {:?}, count: {} = bytes_read {}, total_written {}", + out_fd, in_fd, offset_ptr, count, bytes_read, total_written + ); + return Ok(total_written); } - return Ok(bytes_read); } impl Process { @@ -743,6 +808,12 @@ impl Process { _ => Err(SysError::EBADF), } } + pub fn get_file_const(&self, fd: usize) -> Result<&FileHandle, SysError> { + match self.files.get(&fd).ok_or(SysError::EBADF)? { + FileLike::File(file) => Ok(file), + _ => Err(SysError::EBADF), + } + } /// Lookup INode from the process. /// /// - If `path` is relative, then it is interpreted relative to the directory @@ -764,6 +835,23 @@ impl Process { "lookup_inode_at: dirfd: {:?}, cwd: {:?}, path: {:?}, follow: {:?}", dirfd as isize, self.cwd, path, follow ); + // hard code special path + match path { + "/proc/self/exe" => { + return Ok(Arc::new(Pseudo::new(&self.exec_path, FileType::SymLink))); + } + _ => {} + } + let (fd_dir_path, fd_name) = split_path(&path); + match fd_dir_path { + "/proc/self/fd" => { + let fd: usize = fd_name.parse().map_err(|_| SysError::EINVAL)?; + let fd_path = &self.get_file_const(fd)?.path; + return Ok(Arc::new(Pseudo::new(fd_path, FileType::SymLink))); + } + _ => {} + } + let follow_max_depth = if follow { FOLLOW_MAX_DEPTH } else { 0 }; if dirfd == AT_FDCWD { Ok(ROOT_INODE @@ -858,7 +946,6 @@ impl OpenFlags { } } -#[derive(Debug)] #[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes. pub struct LinuxDirent64 { /// Inode number @@ -873,18 +960,20 @@ pub struct LinuxDirent64 { name: [u8; 0], } -struct DirentBufWriter { +struct DirentBufWriter<'a> { + buf: &'a mut [u8], ptr: *mut LinuxDirent64, rest_size: usize, written_size: usize, } -impl DirentBufWriter { - unsafe fn new(buf: *mut LinuxDirent64, size: usize) -> Self { +impl<'a> DirentBufWriter<'a> { + fn new(buf: &'a mut [u8]) -> Self { DirentBufWriter { - ptr: buf, - rest_size: size, + ptr: buf.as_mut_ptr() as *mut LinuxDirent64, + rest_size: buf.len(), written_size: 0, + buf, } } fn try_write(&mut self, inode: u64, type_: u8, name: &str) -> bool { @@ -1232,28 +1321,28 @@ pub struct IoVec { pub struct IoVecs(Vec<&'static mut [u8]>); impl IoVecs { - pub fn check_and_new( + pub unsafe fn check_and_new( iov_ptr: *const IoVec, iov_count: usize, vm: &MemorySet, readv: bool, ) -> Result { - vm.check_read_array(iov_ptr, iov_count)?; - let iovs = unsafe { slice::from_raw_parts(iov_ptr, iov_count) }.to_vec(); + let iovs = vm.check_read_array(iov_ptr, iov_count)?.to_vec(); // check all bufs in iov for iov in iovs.iter() { - if iov.len > 0 { - // skip empty iov - if readv { - vm.check_write_array(iov.base, iov.len)?; - } else { - vm.check_read_array(iov.base, iov.len)?; - } + // skip empty iov + if iov.len == 0 { + continue; + } + if readv { + vm.check_write_array(iov.base, iov.len)?; + } else { + vm.check_read_array(iov.base, iov.len)?; } } let slices = iovs .iter() - .map(|iov| unsafe { slice::from_raw_parts_mut(iov.base, iov.len) }) + .map(|iov| slice::from_raw_parts_mut(iov.base, iov.len)) .collect(); Ok(IoVecs(slices)) } @@ -1335,12 +1424,12 @@ impl FdSet { }) } else { let len = (nfds + FD_PER_ITEM - 1) / FD_PER_ITEM; - vm.check_write_array(addr, len)?; if len > MAX_FDSET_SIZE { return Err(SysError::EINVAL); } - let slice = unsafe { slice::from_raw_parts_mut(addr, len) }; + let slice = unsafe { vm.check_write_array(addr, len)? }; let bitset: &'static mut BitSlice = slice.into(); + debug!("bitset {:?}", bitset); // save the fdset, and clear it use alloc::borrow::ToOwned; @@ -1364,8 +1453,13 @@ impl FdSet { /// Check to see whether `fd` is in original `FdSet` /// Fd should be less than nfds fn contains(&self, fd: usize) -> bool { - self.origin[fd] + if fd < self.bitset.len() { + self.origin[fd] + } else { + false + } } } +/// Pathname is interpreted relative to the current working directory(CWD) const AT_FDCWD: usize = -100isize as usize; diff --git a/kernel/src/syscall/mem.rs b/kernel/src/syscall/mem.rs index 0b471c25..3a3d4a8f 100644 --- a/kernel/src/syscall/mem.rs +++ b/kernel/src/syscall/mem.rs @@ -1,4 +1,4 @@ -use rcore_memory::memory_set::handler::{ByFrame, Delay}; +use rcore_memory::memory_set::handler::{Delay, File}; use rcore_memory::memory_set::MemoryAttr; use rcore_memory::paging::PageTable; use rcore_memory::Page; @@ -8,108 +8,95 @@ use crate::memory::GlobalFrameAlloc; use super::*; -pub fn sys_mmap( - mut addr: usize, - len: usize, - prot: usize, - flags: usize, - fd: usize, - offset: usize, -) -> SysResult { - let prot = MmapProt::from_bits_truncate(prot); - let flags = MmapFlags::from_bits_truncate(flags); - info!( - "mmap: addr={:#x}, size={:#x}, prot={:?}, flags={:?}, fd={}, offset={:#x}", - addr, len, prot, flags, fd, offset - ); - - let mut proc = process(); - if addr == 0 { - // although NULL can be a valid address - // but in C, NULL is regarded as allocation failure - // so just skip it - addr = PAGE_SIZE; - } - - if flags.contains(MmapFlags::FIXED) { - // we have to map it to addr, so remove the old mapping first - proc.vm.pop_with_split(addr, addr + len); - } else { - addr = proc.vm.find_free_area(addr, len); - } - - if flags.contains(MmapFlags::ANONYMOUS) { - if flags.contains(MmapFlags::SHARED) { - return Err(SysError::EINVAL); - } - proc.vm.push( - addr, - addr + len, - prot.to_attr(), - Delay::new(GlobalFrameAlloc), - "mmap_anon", +impl Syscall<'_> { + pub fn sys_mmap( + &mut self, + mut addr: usize, + len: usize, + prot: usize, + flags: usize, + fd: usize, + offset: usize, + ) -> SysResult { + let prot = MmapProt::from_bits_truncate(prot); + let flags = MmapFlags::from_bits_truncate(flags); + info!( + "mmap: addr={:#x}, size={:#x}, prot={:?}, flags={:?}, fd={}, offset={:#x}", + addr, len, prot, flags, fd, offset ); - return Ok(addr); - } else { - // only check - let _ = proc.get_file(fd)?; - // TODO: delay mmap file - proc.vm.push( - addr, - addr + len, - prot.to_attr(), - ByFrame::new(GlobalFrameAlloc), - "mmap_file", + let mut proc = self.process(); + if addr == 0 { + // although NULL can be a valid address + // but in C, NULL is regarded as allocation failure + // so just skip it + addr = PAGE_SIZE; + } + + if flags.contains(MmapFlags::FIXED) { + // we have to map it to addr, so remove the old mapping first + self.vm().pop_with_split(addr, addr + len); + } else { + addr = self.vm().find_free_area(addr, len); + } + + if flags.contains(MmapFlags::ANONYMOUS) { + if flags.contains(MmapFlags::SHARED) { + return Err(SysError::EINVAL); + } + self.vm().push( + addr, + addr + len, + prot.to_attr(), + Delay::new(GlobalFrameAlloc), + "mmap_anon", + ); + return Ok(addr); + } else { + let inode = proc.get_file(fd)?.inode(); + self.vm().push( + addr, + addr + len, + prot.to_attr(), + File { + file: INodeForMap(inode), + mem_start: addr, + file_start: offset, + file_end: offset + len, + allocator: GlobalFrameAlloc, + }, + "mmap_file", + ); + return Ok(addr); + } + } + + pub fn sys_mprotect(&mut self, addr: usize, len: usize, prot: usize) -> SysResult { + let prot = MmapProt::from_bits_truncate(prot); + info!( + "mprotect: addr={:#x}, size={:#x}, prot={:?}", + addr, len, prot ); - let data = unsafe { slice::from_raw_parts_mut(addr as *mut u8, len) }; - let file = proc.get_file(fd)?; - let read_len = file.read_at(offset, data)?; - if read_len != data.len() { - // use count() to consume the iterator - data[read_len..].iter_mut().map(|x| *x = 0).count(); + let attr = prot.to_attr(); + + // FIXME: properly set the attribute of the area + // now some mut ptr check is fault + let vm = self.vm(); + let memory_area = vm + .iter() + .find(|area| area.is_overlap_with(addr, addr + len)); + if memory_area.is_none() { + return Err(SysError::ENOMEM); } - return Ok(addr); + Ok(0) + } + + pub fn sys_munmap(&mut self, addr: usize, len: usize) -> SysResult { + info!("munmap addr={:#x}, size={:#x}", addr, len); + self.vm().pop_with_split(addr, addr + len); + Ok(0) } } - -pub fn sys_mprotect(addr: usize, len: usize, prot: usize) -> SysResult { - let prot = MmapProt::from_bits_truncate(prot); - info!( - "mprotect: addr={:#x}, size={:#x}, prot={:?}", - addr, len, prot - ); - - let mut proc = process(); - let attr = prot.to_attr(); - - // FIXME: properly set the attribute of the area - // now some mut ptr check is fault - let memory_area = proc - .vm - .iter() - .find(|area| area.is_overlap_with(addr, addr + len)); - if memory_area.is_none() { - return Err(SysError::ENOMEM); - } - proc.vm.edit(|pt| { - for page in Page::range_of(addr, addr + len) { - let entry = pt - .get_entry(page.start_address()) - .expect("failed to get entry"); - attr.apply(entry); - } - }); - Ok(0) -} - -pub fn sys_munmap(addr: usize, len: usize) -> SysResult { - info!("munmap addr={:#x}, size={:#x}", addr, len); - let mut proc = process(); - proc.vm.pop_with_split(addr, addr + len); - Ok(0) -} - bitflags! { pub struct MmapProt: usize { /// Data cannot be accessed diff --git a/kernel/src/syscall/misc.rs b/kernel/src/syscall/misc.rs index ac3ff39e..e504aa8a 100644 --- a/kernel/src/syscall/misc.rs +++ b/kernel/src/syscall/misc.rs @@ -4,121 +4,185 @@ use crate::consts::USER_STACK_SIZE; use core::mem::size_of; use core::sync::atomic::{AtomicI32, Ordering}; -pub fn sys_arch_prctl(code: i32, addr: usize, tf: &mut TrapFrame) -> SysResult { - const ARCH_SET_FS: i32 = 0x1002; - match code { - #[cfg(target_arch = "x86_64")] - ARCH_SET_FS => { - info!("sys_arch_prctl: set FS to {:#x}", addr); - tf.fsbase = addr; - Ok(0) - } - _ => Err(SysError::EINVAL), - } -} - -pub fn sys_uname(buf: *mut u8) -> SysResult { - info!("sched_uname: buf: {:?}", buf); - - let offset = 65; - let strings = ["rCore", "orz", "0.1.0", "1", "machine", "domain"]; - let proc = process(); - proc.vm.check_write_array(buf, strings.len() * offset)?; - - for i in 0..strings.len() { - unsafe { - util::write_cstr(buf.add(i * offset), &strings[i]); - } - } - Ok(0) -} - -pub fn sys_sched_getaffinity(pid: usize, size: usize, mask: *mut u32) -> SysResult { - info!( - "sched_getaffinity: pid: {}, size: {}, mask: {:?}", - pid, size, mask - ); - let proc = process(); - proc.vm.check_write_array(mask, size / size_of::())?; - - // we only have 4 cpu at most. - // so just set it. - unsafe { - *mask = 0b1111; - } - Ok(0) -} - -pub fn sys_sysinfo(sys_info: *mut SysInfo) -> SysResult { - let proc = process(); - proc.vm.check_write_ptr(sys_info)?; - - let sysinfo = SysInfo::default(); - unsafe { *sys_info = sysinfo }; - Ok(0) -} - -pub fn sys_futex(uaddr: usize, op: u32, val: i32, timeout: *const TimeSpec) -> SysResult { - info!( - "futex: [{}] uaddr: {:#x}, op: {:#x}, val: {}, timeout_ptr: {:?}", - thread::current().id(), - uaddr, - op, - val, - timeout - ); - // if op & OP_PRIVATE == 0 { - // unimplemented!("futex only support process-private"); - // return Err(SysError::ENOSYS); - // } - if uaddr % size_of::() != 0 { - return Err(SysError::EINVAL); - } - process().vm.check_write_ptr(uaddr as *mut AtomicI32)?; - let atomic = unsafe { &mut *(uaddr as *mut AtomicI32) }; - let _timeout = if timeout.is_null() { - None - } else { - process().vm.check_read_ptr(timeout)?; - Some(unsafe { *timeout }) - }; - - const OP_WAIT: u32 = 0; - const OP_WAKE: u32 = 1; - const OP_PRIVATE: u32 = 128; - - let queue = process().get_futex(uaddr); - - match op & 0xf { - OP_WAIT => { - if atomic.load(Ordering::Acquire) != val { - return Err(SysError::EAGAIN); +impl Syscall<'_> { + #[cfg(target_arch = "x86_64")] + pub fn sys_arch_prctl(&mut self, code: i32, addr: usize) -> SysResult { + const ARCH_SET_FS: i32 = 0x1002; + match code { + ARCH_SET_FS => { + info!("sys_arch_prctl: set FSBASE to {:#x}", addr); + self.tf.fsbase = addr; + Ok(0) } - // FIXME: support timeout - queue._wait(); - Ok(0) + _ => Err(SysError::EINVAL), } - OP_WAKE => { - let woken_up_count = queue.notify_n(val as usize); - Ok(woken_up_count) + } + + pub fn sys_uname(&mut self, buf: *mut u8) -> SysResult { + info!("uname: buf: {:?}", buf); + + let offset = 65; + let strings = ["rCore", "orz", "0.1.0", "1", "machine", "domain"]; + let buf = unsafe { self.vm().check_write_array(buf, strings.len() * offset)? }; + + for i in 0..strings.len() { + unsafe { + util::write_cstr(&mut buf[i * offset], &strings[i]); + } } - _ => { - warn!("unsupported futex operation: {}", op); - Err(SysError::ENOSYS) + Ok(0) + } + + pub fn sys_sched_getaffinity(&mut self, pid: usize, size: usize, mask: *mut u32) -> SysResult { + info!( + "sched_getaffinity: pid: {}, size: {}, mask: {:?}", + pid, size, mask + ); + let mask = unsafe { self.vm().check_write_array(mask, size / size_of::())? }; + + // we only have 4 cpu at most. + // so just set it. + mask[0] = 0b1111; + Ok(0) + } + + pub fn sys_sysinfo(&mut self, sys_info: *mut SysInfo) -> SysResult { + let sys_info = unsafe { self.vm().check_write_ptr(sys_info)? }; + + let sysinfo = SysInfo::default(); + *sys_info = sysinfo; + Ok(0) + } + + pub fn sys_futex( + &mut self, + uaddr: usize, + op: u32, + val: i32, + timeout: *const TimeSpec, + ) -> SysResult { + info!( + "futex: [{}] uaddr: {:#x}, op: {:#x}, val: {}, timeout_ptr: {:?}", + thread::current().id(), + uaddr, + op, + val, + timeout + ); + // if op & OP_PRIVATE == 0 { + // unimplemented!("futex only support process-private"); + // return Err(SysError::ENOSYS); + // } + if uaddr % size_of::() != 0 { + return Err(SysError::EINVAL); } + let atomic = unsafe { self.vm().check_write_ptr(uaddr as *mut AtomicI32)? }; + let _timeout = if timeout.is_null() { + None + } else { + Some(unsafe { *self.vm().check_read_ptr(timeout)? }) + }; + + const OP_WAIT: u32 = 0; + const OP_WAKE: u32 = 1; + const OP_PRIVATE: u32 = 128; + + let mut proc = self.process(); + let queue = proc.get_futex(uaddr); + + match op & 0xf { + OP_WAIT => { + if atomic.load(Ordering::Acquire) != val { + return Err(SysError::EAGAIN); + } + // FIXME: support timeout + queue.wait(proc); + Ok(0) + } + OP_WAKE => { + let woken_up_count = queue.notify_n(val as usize); + Ok(woken_up_count) + } + _ => { + warn!("unsupported futex operation: {}", op); + Err(SysError::ENOSYS) + } + } + } + + pub fn sys_reboot(&mut self, _magic: u32, _magic2: u32, cmd: u32, _arg: *const u8) -> SysResult { + // we will skip verifying magic + if cmd == LINUX_REBOOT_CMD_HALT { + unsafe { + cpu::exit_in_qemu(1); + } + } + Ok(0) + } + + pub fn sys_prlimit64( + &mut self, + pid: usize, + resource: usize, + new_limit: *const RLimit, + old_limit: *mut RLimit, + ) -> SysResult { + info!( + "prlimit64: pid: {}, resource: {}, new_limit: {:x?}, old_limit: {:x?}", + pid, resource, new_limit, old_limit + ); + match resource { + RLIMIT_STACK => { + if !old_limit.is_null() { + let old_limit = unsafe { self.vm().check_write_ptr(old_limit)? }; + *old_limit = RLimit { + cur: USER_STACK_SIZE as u64, + max: USER_STACK_SIZE as u64, + }; + } + Ok(0) + } + RLIMIT_NOFILE => { + if !old_limit.is_null() { + let old_limit = unsafe { self.vm().check_write_ptr(old_limit)? }; + *old_limit = RLimit { + cur: 1024, + max: 1024, + }; + } + Ok(0) + } + RLIMIT_RSS | RLIMIT_AS => { + if !old_limit.is_null() { + let old_limit = unsafe { self.vm().check_write_ptr(old_limit)? }; + // 1GB + *old_limit = RLimit { + cur: 1024 * 1024 * 1024, + max: 1024 * 1024 * 1024, + }; + } + Ok(0) + } + _ => Err(SysError::ENOSYS), + } + } + + pub fn sys_getrandom(&mut self, buf: *mut u8, len: usize, _flag: u32) -> SysResult { + //info!("getrandom: buf: {:?}, len: {:?}, falg {:?}", buf, len,flag); + let slice = unsafe { self.vm().check_write_array(buf, len)? }; + let mut i = 0; + for elm in slice { + unsafe { + *elm = i + crate::trap::TICK as u8; + } + i += 1; + } + + Ok(len) } } const LINUX_REBOOT_CMD_HALT: u32 = 0xcdef0123; -pub fn sys_reboot(_magic: u32, magic2: u32, cmd: u32, _arg: *const u8) -> SysResult { - // we will skip verifying magic - if cmd == LINUX_REBOOT_CMD_HALT { - unsafe { - cpu::exit_in_qemu(1); - } - } - Ok(0) -} #[repr(C)] #[derive(Debug, Default)] @@ -142,59 +206,6 @@ const RLIMIT_RSS: usize = 5; const RLIMIT_NOFILE: usize = 7; const RLIMIT_AS: usize = 9; -pub fn sys_prlimit64( - pid: usize, - resource: usize, - new_limit: *const RLimit, - old_limit: *mut RLimit, -) -> SysResult { - let proc = process(); - info!( - "prlimit64: pid: {}, resource: {}, new_limit: {:x?}, old_limit: {:x?}", - pid, resource, new_limit, old_limit - ); - match resource { - RLIMIT_STACK => { - if !old_limit.is_null() { - proc.vm.check_write_ptr(old_limit)?; - unsafe { - *old_limit = RLimit { - cur: USER_STACK_SIZE as u64, - max: USER_STACK_SIZE as u64, - }; - } - } - Ok(0) - } - RLIMIT_NOFILE => { - if !old_limit.is_null() { - proc.vm.check_write_ptr(old_limit)?; - unsafe { - *old_limit = RLimit { - cur: 1024, - max: 1024, - }; - } - } - Ok(0) - } - RLIMIT_RSS | RLIMIT_AS => { - if !old_limit.is_null() { - proc.vm.check_write_ptr(old_limit)?; - unsafe { - // 1GB - *old_limit = RLimit { - cur: 1024 * 1024 * 1024, - max: 1024 * 1024 * 1024, - }; - } - } - Ok(0) - } - _ => Err(SysError::ENOSYS), - } -} - #[repr(C)] #[derive(Debug, Default)] pub struct RLimit { diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index c78351a8..f637648a 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -10,8 +10,9 @@ use rcore_memory::VMError; use crate::arch::cpu; use crate::arch::interrupt::TrapFrame; use crate::arch::syscall::*; +use crate::memory::MemorySet; use crate::process::*; -use crate::sync::Condvar; +use crate::sync::{Condvar, MutexGuard, SpinNoIrq}; use crate::thread; use crate::util; @@ -31,428 +32,382 @@ mod net; mod proc; mod time; +use alloc::collections::BTreeMap; +use spin::Mutex; + +#[cfg(feature = "profile")] +lazy_static! { + static ref SYSCALL_TIMING: Mutex> = Mutex::new(BTreeMap::new()); +} + /// System call dispatcher -// This #[deny(unreachable_patterns)] checks if each match arm is defined -// See discussion in https://github.com/oscourse-tsinghua/rcore_plus/commit/17e644e54e494835f1a49b34b80c2c4f15ed0dbe. -#[deny(unreachable_patterns)] pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize { - let cid = cpu::id(); - let pid = { process().pid.clone() }; - let tid = processor().tid(); - if !pid.is_init() { - // we trust pid 0 process - debug!("{}:{}:{} syscall id {} begin", cid, pid, tid, id); - } - - // use platform-specific syscal numbers - // See https://filippo.io/linux-syscall-table/ - // And https://fedora.juszkiewicz.com.pl/syscalls.html. - let ret = match id { - // 0 - SYS_READ => sys_read(args[0], args[1] as *mut u8, args[2]), - SYS_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), - SYS_CLOSE => sys_close(args[0]), - SYS_FSTAT => sys_fstat(args[0], args[1] as *mut Stat), - SYS_LSEEK => sys_lseek(args[0], args[1] as i64, args[2] as u8), - SYS_MMAP => sys_mmap(args[0], args[1], args[2], args[3], args[4], args[5]), - // 10 - SYS_MPROTECT => sys_mprotect(args[0], args[1], args[2]), - SYS_MUNMAP => sys_munmap(args[0], args[1]), - SYS_BRK => { - warn!("sys_brk is unimplemented, return -1"); - Err(SysError::ENOMEM) - } - SYS_RT_SIGACTION => { - warn!("sys_sigaction is unimplemented"); - Ok(0) - } - SYS_RT_SIGPROCMASK => { - warn!("sys_sigprocmask is unimplemented"); - Ok(0) - } - SYS_IOCTL => sys_ioctl(args[0], args[1], args[2], args[3], args[4]), - SYS_PREAD64 => sys_pread(args[0], args[1] as *mut u8, args[2], args[3]), - SYS_PWRITE64 => sys_pwrite(args[0], args[1] as *const u8, args[2], args[3]), - SYS_READV => sys_readv(args[0], args[1] as *const IoVec, args[2]), - // 20 - SYS_WRITEV => sys_writev(args[0], args[1] as *const IoVec, args[2]), - SYS_SCHED_YIELD => sys_yield(), - SYS_MADVISE => { - warn!("sys_madvise is unimplemented"); - Ok(0) - } - SYS_NANOSLEEP => sys_nanosleep(args[0] as *const TimeSpec), - SYS_SETITIMER => { - warn!("sys_setitimer is unimplemented"); - Ok(0) - } - SYS_GETPID => sys_getpid(), - // 40 - SYS_SENDFILE => sys_sendfile(args[0], args[1], args[2] as *mut usize, args[3]), - SYS_SOCKET => sys_socket(args[0], args[1], args[2]), - SYS_CONNECT => sys_connect(args[0], args[1] as *const SockAddr, args[2]), - SYS_ACCEPT => sys_accept(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), - SYS_SENDTO => sys_sendto( - args[0], - args[1] as *const u8, - args[2], - args[3], - args[4] as *const SockAddr, - args[5], - ), - SYS_RECVFROM => sys_recvfrom( - args[0], - args[1] as *mut u8, - args[2], - args[3], - args[4] as *mut SockAddr, - args[5] as *mut u32, - ), - // SYS_SENDMSG => sys_sendmsg(), - SYS_RECVMSG => sys_recvmsg(args[0], args[1] as *mut MsgHdr, args[2]), - SYS_SHUTDOWN => sys_shutdown(args[0], args[1]), - SYS_BIND => sys_bind(args[0], args[1] as *const SockAddr, args[2]), - // 50 - SYS_LISTEN => sys_listen(args[0], args[1]), - SYS_GETSOCKNAME => sys_getsockname(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), - SYS_GETPEERNAME => sys_getpeername(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), - SYS_SETSOCKOPT => sys_setsockopt(args[0], args[1], args[2], args[3] as *const u8, args[4]), - SYS_GETSOCKOPT => sys_getsockopt( - args[0], - args[1], - args[2], - args[3] as *mut u8, - args[4] as *mut u32, - ), - SYS_CLONE => sys_clone( - args[0], - args[1], - args[2] as *mut u32, - args[3] as *mut u32, - args[4], - tf, - ), - SYS_EXECVE => sys_exec( - args[0] as *const u8, - args[1] as *const *const u8, - args[2] as *const *const u8, - tf, - ), - // 60 - SYS_EXIT => sys_exit(args[0] as usize), - SYS_WAIT4 => sys_wait4(args[0] as isize, args[1] as *mut i32), // TODO: wait4 - SYS_KILL => sys_kill(args[0], args[1]), - SYS_UNAME => sys_uname(args[0] as *mut u8), - SYS_FCNTL => { - warn!("sys_fcntl is unimplemented"); - Ok(0) - } - SYS_FLOCK => { - warn!("sys_flock is unimplemented"); - Ok(0) - } - SYS_FSYNC => sys_fsync(args[0]), - SYS_FDATASYNC => sys_fdatasync(args[0]), - SYS_TRUNCATE => sys_truncate(args[0] as *const u8, args[1]), - SYS_FTRUNCATE => sys_ftruncate(args[0], args[1]), - SYS_GETCWD => sys_getcwd(args[0] as *mut u8, args[1]), - // 80 - SYS_CHDIR => sys_chdir(args[0] as *const u8), - SYS_FCHMOD => { - warn!("sys_fchmod is unimplemented"); - Ok(0) - } - SYS_FCHOWN => { - warn!("sys_fchown is unimplemented"); - Ok(0) - } - SYS_UMASK => { - warn!("sys_umask is unimplemented"); - Ok(0o777) - } - SYS_GETTIMEOFDAY => sys_gettimeofday(args[0] as *mut TimeVal, args[1] as *const u8), - // SYS_GETRLIMIT => sys_getrlimit(), - SYS_GETRUSAGE => sys_getrusage(args[0], args[1] as *mut RUsage), - SYS_SYSINFO => sys_sysinfo(args[0] as *mut SysInfo), - SYS_TIMES => sys_times(args[0] as *mut Tms), - SYS_GETUID => { - warn!("sys_getuid is unimplemented"); - Ok(0) - } - SYS_GETGID => { - warn!("sys_getgid is unimplemented"); - Ok(0) - } - SYS_SETUID => { - warn!("sys_setuid is unimplemented"); - Ok(0) - } - SYS_GETEUID => { - warn!("sys_geteuid is unimplemented"); - Ok(0) - } - SYS_GETEGID => { - warn!("sys_getegid is unimplemented"); - Ok(0) - } - SYS_SETPGID => { - warn!("sys_setpgid is unimplemented"); - Ok(0) - } - // 110 - SYS_GETPPID => sys_getppid(), - SYS_SETSID => { - warn!("sys_setsid is unimplemented"); - Ok(0) - } - SYS_GETPGID => { - warn!("sys_getpgid is unimplemented"); - Ok(0) - } - SYS_GETGROUPS => { - warn!("sys_getgroups is unimplemented"); - Ok(0) - } - SYS_SETGROUPS => { - warn!("sys_setgroups is unimplemented"); - Ok(0) - } - SYS_SIGALTSTACK => { - warn!("sys_sigaltstack is unimplemented"); - Ok(0) - } - SYS_STATFS => { - warn!("statfs is unimplemented"); - Err(SysError::EACCES) - } - SYS_FSTATFS => { - warn!("fstatfs is unimplemented"); - Err(SysError::EACCES) - } - SYS_SETPRIORITY => sys_set_priority(args[0]), - SYS_PRCTL => { - warn!("prctl is unimplemented"); - Ok(0) - } - // SYS_SETRLIMIT => sys_setrlimit(), - SYS_SYNC => sys_sync(), - SYS_MOUNT => { - warn!("mount is unimplemented"); - Err(SysError::EACCES) - } - SYS_UMOUNT2 => { - warn!("umount2 is unimplemented"); - Err(SysError::EACCES) - } - SYS_REBOOT => sys_reboot( - args[0] as u32, - args[1] as u32, - args[2] as u32, - args[3] as *const u8, - ), - SYS_GETTID => sys_gettid(), - SYS_FUTEX => sys_futex( - args[0], - args[1] as u32, - args[2] as i32, - args[3] as *const TimeSpec, - ), - SYS_SCHED_GETAFFINITY => sys_sched_getaffinity(args[0], args[1], args[2] as *mut u32), - SYS_GETDENTS64 => sys_getdents64(args[0], args[1] as *mut LinuxDirent64, args[2]), - SYS_SET_TID_ADDRESS => { - warn!("sys_set_tid_address is unimplemented"); - Ok(thread::current().id()) - } - SYS_CLOCK_GETTIME => sys_clock_gettime(args[0], args[1] as *mut TimeSpec), - SYS_EXIT_GROUP => sys_exit_group(args[0]), - SYS_OPENAT => sys_openat(args[0], args[1] as *const u8, args[2], args[3]), - SYS_MKDIRAT => sys_mkdirat(args[0], args[1] as *const u8, args[2]), - // SYS_MKNODAT => sys_mknod(), - // 260 - SYS_FCHOWNAT => { - warn!("sys_fchownat is unimplemented"); - Ok(0) - } - SYS_NEWFSTATAT => sys_fstatat(args[0], args[1] as *const u8, args[2] as *mut Stat, args[3]), - SYS_UNLINKAT => sys_unlinkat(args[0], args[1] as *const u8, args[2]), - SYS_RENAMEAT => sys_renameat(args[0], args[1] as *const u8, args[2], args[3] as *const u8), - SYS_LINKAT => sys_linkat( - args[0], - args[1] as *const u8, - args[2], - args[3] as *const u8, - args[4], - ), - SYS_SYMLINKAT => Err(SysError::EACCES), - SYS_READLINKAT => { - sys_readlinkat(args[0], args[1] as *const u8, args[2] as *mut u8, args[3]) - } - SYS_FCHMODAT => { - warn!("sys_fchmodat is unimplemented"); - Ok(0) - } - SYS_FACCESSAT => sys_faccessat(args[0], args[1] as *const u8, args[2], args[3]), - SYS_PPOLL => sys_ppoll(args[0] as *mut PollFd, args[1], args[2] as *const TimeSpec), // ignore sigmask - // 280 - SYS_UTIMENSAT => { - warn!("sys_utimensat is unimplemented"); - Ok(0) - } - SYS_ACCEPT4 => sys_accept(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), // use accept for accept4 - SYS_EPOLL_CREATE1 => { - warn!("sys_epoll_create1 is unimplemented"); - Err(SysError::ENOSYS) - } - SYS_DUP3 => sys_dup2(args[0], args[1]), // TODO: handle `flags` - SYS_PIPE2 => sys_pipe(args[0] as *mut u32), // TODO: handle `flags` - SYS_PRLIMIT64 => sys_prlimit64( - args[0], - args[1], - args[2] as *const RLimit, - args[3] as *mut RLimit, - ), - // custom temporary syscall - SYS_MAP_PCI_DEVICE => sys_map_pci_device(args[0], args[1]), - SYS_GET_PADDR => sys_get_paddr(args[0] as *const u64, args[1] as *mut u64, args[2]), - - _ => { - #[cfg(target_arch = "x86_64")] - let x86_64_ret = x86_64_syscall(id, args, tf); - #[cfg(not(target_arch = "x86_64"))] - let x86_64_ret = None; - - #[cfg(target_arch = "mips")] - let mips_ret = mips_syscall(id, args, tf); - #[cfg(not(target_arch = "mips"))] - let mips_ret = None; - if let Some(ret) = x86_64_ret { - ret - } else if let Some(ret) = mips_ret { - ret - } else { - error!("unknown syscall id: {}, args: {:x?}", id, args); - crate::trap::error(tf); - } - } - }; - if !pid.is_init() { - // we trust pid 0 process - debug!( - "{}:{}:{} syscall id {} ret with {:x?}", - cid, pid, tid, id, ret - ); - } - match ret { - Ok(code) => code as isize, - Err(err) => -(err as isize), - } + let thread = unsafe { current_thread() }; + let mut syscall = Syscall { thread, tf }; + syscall.syscall(id, args) } -#[cfg(target_arch = "mips")] -fn mips_syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> Option { - let ret = match id { - SYS_OPEN => sys_open(args[0] as *const u8, args[1], args[2]), - SYS_POLL => sys_poll(args[0] as *mut PollFd, args[1], args[2]), - SYS_DUP2 => sys_dup2(args[0], args[1]), - SYS_FORK => sys_fork(tf), - SYS_MMAP2 => sys_mmap(args[0], args[1], args[2], args[3], args[4], args[5] * 4096), - SYS_FSTAT64 => sys_fstat(args[0], args[1] as *mut Stat), - SYS_LSTAT64 => sys_lstat(args[0] as *const u8, args[1] as *mut Stat), - SYS_STAT64 => sys_stat(args[0] as *const u8, args[1] as *mut Stat), - SYS_PIPE => { - let fd_ptr = args[0] as *mut u32; - match sys_pipe(fd_ptr) { - Ok(code) => { - unsafe { - tf.v0 = *fd_ptr as usize; - tf.v1 = *(fd_ptr.add(1)) as usize; - } - Ok(tf.v0) +/// All context needed for syscall +struct Syscall<'a> { + thread: &'a mut Thread, + tf: &'a mut TrapFrame, +} + +impl Syscall<'_> { + /// Get current process + pub fn process(&self) -> MutexGuard<'_, Process, SpinNoIrq> { + self.thread.proc.lock() + } + + /// Get current virtual memory + pub fn vm(&self) -> MutexGuard<'_, MemorySet, SpinNoIrq> { + self.thread.vm.lock() + } + + /// System call dispatcher + // This #[deny(unreachable_patterns)] checks if each match arm is defined + // See discussion in https://github.com/oscourse-tsinghua/rcore_plus/commit/17e644e54e494835f1a49b34b80c2c4f15ed0dbe. + #[deny(unreachable_patterns)] + fn syscall(&mut self, id: usize, args: [usize; 6]) -> isize { + #[cfg(feature = "profile")] + let begin_time = unsafe { core::arch::x86_64::_rdtsc() }; + let cid = cpu::id(); + let pid = self.process().pid.clone(); + let tid = processor().tid(); + if !pid.is_init() { + // we trust pid 0 process + debug!("{}:{}:{} syscall id {} begin", cid, pid, tid, id); + } + + // use platform-specific syscal numbers + // See https://filippo.io/linux-syscall-table/ + // And https://fedora.juszkiewicz.com.pl/syscalls.html. + let ret = match id { + // file + SYS_READ => self.sys_read(args[0], args[1] as *mut u8, args[2]), + SYS_WRITE => self.sys_write(args[0], args[1] as *const u8, args[2]), + SYS_OPENAT => self.sys_openat(args[0], args[1] as *const u8, args[2], args[3]), + SYS_CLOSE => self.sys_close(args[0]), + SYS_FSTAT => self.sys_fstat(args[0], args[1] as *mut Stat), + SYS_NEWFSTATAT => { + self.sys_fstatat(args[0], args[1] as *const u8, args[2] as *mut Stat, args[3]) + } + SYS_LSEEK => self.sys_lseek(args[0], args[1] as i64, args[2] as u8), + SYS_IOCTL => self.sys_ioctl(args[0], args[1], args[2], args[3], args[4]), + SYS_PREAD64 => self.sys_pread(args[0], args[1] as *mut u8, args[2], args[3]), + SYS_PWRITE64 => self.sys_pwrite(args[0], args[1] as *const u8, args[2], args[3]), + SYS_READV => self.sys_readv(args[0], args[1] as *const IoVec, args[2]), + SYS_WRITEV => self.sys_writev(args[0], args[1] as *const IoVec, args[2]), + SYS_SENDFILE => self.sys_sendfile(args[0], args[1], args[2] as *mut usize, args[3]), + SYS_FCNTL => self.unimplemented("fcntl", Ok(0)), + SYS_FLOCK => self.unimplemented("flock", Ok(0)), + SYS_FSYNC => self.sys_fsync(args[0]), + SYS_FDATASYNC => self.sys_fdatasync(args[0]), + SYS_TRUNCATE => self.sys_truncate(args[0] as *const u8, args[1]), + SYS_FTRUNCATE => self.sys_ftruncate(args[0], args[1]), + SYS_GETDENTS64 => self.sys_getdents64(args[0], args[1] as *mut LinuxDirent64, args[2]), + SYS_GETCWD => self.sys_getcwd(args[0] as *mut u8, args[1]), + SYS_CHDIR => self.sys_chdir(args[0] as *const u8), + SYS_RENAMEAT => { + self.sys_renameat(args[0], args[1] as *const u8, args[2], args[3] as *const u8) + } + SYS_MKDIRAT => self.sys_mkdirat(args[0], args[1] as *const u8, args[2]), + SYS_LINKAT => self.sys_linkat( + args[0], + args[1] as *const u8, + args[2], + args[3] as *const u8, + args[4], + ), + SYS_UNLINKAT => self.sys_unlinkat(args[0], args[1] as *const u8, args[2]), + SYS_SYMLINKAT => self.unimplemented("symlinkat", Err(SysError::EACCES)), + SYS_READLINKAT => { + self.sys_readlinkat(args[0], args[1] as *const u8, args[2] as *mut u8, args[3]) + } + SYS_FCHMOD => self.unimplemented("fchmod", Ok(0)), + SYS_FCHMODAT => self.unimplemented("fchmodat", Ok(0)), + SYS_FCHOWN => self.unimplemented("fchown", Ok(0)), + SYS_FCHOWNAT => self.unimplemented("fchownat", Ok(0)), + SYS_FACCESSAT => self.sys_faccessat(args[0], args[1] as *const u8, args[2], args[3]), + SYS_DUP3 => self.sys_dup2(args[0], args[1]), // TODO: handle `flags` + SYS_PIPE2 => self.sys_pipe(args[0] as *mut u32), // TODO: handle `flags` + SYS_UTIMENSAT => self.unimplemented("utimensat", Ok(0)), + + // io multiplexing + SYS_PPOLL => { + self.sys_ppoll(args[0] as *mut PollFd, args[1], args[2] as *const TimeSpec) + } // ignore sigmask + SYS_EPOLL_CREATE1 => self.unimplemented("epoll_create1", Err(SysError::ENOSYS)), + + // file system + SYS_STATFS => self.unimplemented("statfs", Err(SysError::EACCES)), + SYS_FSTATFS => self.unimplemented("fstatfs", Err(SysError::EACCES)), + SYS_SYNC => self.sys_sync(), + SYS_MOUNT => self.unimplemented("mount", Err(SysError::EACCES)), + SYS_UMOUNT2 => self.unimplemented("umount2", Err(SysError::EACCES)), + + // memory + SYS_BRK => self.unimplemented("brk", Err(SysError::ENOMEM)), + SYS_MMAP => self.sys_mmap(args[0], args[1], args[2], args[3], args[4], args[5]), + SYS_MPROTECT => self.sys_mprotect(args[0], args[1], args[2]), + SYS_MUNMAP => self.sys_munmap(args[0], args[1]), + SYS_MADVISE => self.unimplemented("madvise", Ok(0)), + + // signal + SYS_RT_SIGACTION => self.unimplemented("sigaction", Ok(0)), + SYS_RT_SIGPROCMASK => self.unimplemented("sigprocmask", Ok(0)), + SYS_SIGALTSTACK => self.unimplemented("sigaltstack", Ok(0)), + SYS_KILL => self.sys_kill(args[0], args[1]), + + // schedule + SYS_SCHED_YIELD => self.sys_yield(), + SYS_SCHED_GETAFFINITY => { + self.sys_sched_getaffinity(args[0], args[1], args[2] as *mut u32) + } + + // socket + SYS_SOCKET => self.sys_socket(args[0], args[1], args[2]), + SYS_CONNECT => self.sys_connect(args[0], args[1] as *const SockAddr, args[2]), + SYS_ACCEPT => self.sys_accept(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), + SYS_ACCEPT4 => self.sys_accept(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), // use accept for accept4 + SYS_SENDTO => self.sys_sendto( + args[0], + args[1] as *const u8, + args[2], + args[3], + args[4] as *const SockAddr, + args[5], + ), + SYS_RECVFROM => self.sys_recvfrom( + args[0], + args[1] as *mut u8, + args[2], + args[3], + args[4] as *mut SockAddr, + args[5] as *mut u32, + ), + // SYS_SENDMSG => self.sys_sendmsg(), + SYS_RECVMSG => self.sys_recvmsg(args[0], args[1] as *mut MsgHdr, args[2]), + SYS_SHUTDOWN => self.sys_shutdown(args[0], args[1]), + SYS_BIND => self.sys_bind(args[0], args[1] as *const SockAddr, args[2]), + SYS_LISTEN => self.sys_listen(args[0], args[1]), + SYS_GETSOCKNAME => { + self.sys_getsockname(args[0], args[1] as *mut SockAddr, args[2] as *mut u32) + } + SYS_GETPEERNAME => { + self.sys_getpeername(args[0], args[1] as *mut SockAddr, args[2] as *mut u32) + } + SYS_SETSOCKOPT => { + self.sys_setsockopt(args[0], args[1], args[2], args[3] as *const u8, args[4]) + } + SYS_GETSOCKOPT => self.sys_getsockopt( + args[0], + args[1], + args[2], + args[3] as *mut u8, + args[4] as *mut u32, + ), + + // process + SYS_CLONE => self.sys_clone( + args[0], + args[1], + args[2] as *mut u32, + args[3] as *mut u32, + args[4], + ), + SYS_EXECVE => self.sys_exec( + args[0] as *const u8, + args[1] as *const *const u8, + args[2] as *const *const u8, + ), + SYS_EXIT => self.sys_exit(args[0] as usize), + SYS_EXIT_GROUP => self.sys_exit_group(args[0]), + SYS_WAIT4 => self.sys_wait4(args[0] as isize, args[1] as *mut i32), // TODO: wait4 + SYS_SET_TID_ADDRESS => self.sys_set_tid_address(args[0] as *mut u32), + SYS_FUTEX => self.sys_futex( + args[0], + args[1] as u32, + args[2] as i32, + args[3] as *const TimeSpec, + ), + + // time + SYS_NANOSLEEP => self.sys_nanosleep(args[0] as *const TimeSpec), + SYS_SETITIMER => self.unimplemented("setitimer", Ok(0)), + SYS_GETTIMEOFDAY => { + self.sys_gettimeofday(args[0] as *mut TimeVal, args[1] as *const u8) + } + SYS_CLOCK_GETTIME => self.sys_clock_gettime(args[0], args[1] as *mut TimeSpec), + + // system + SYS_GETPID => self.sys_getpid(), + SYS_GETTID => self.sys_gettid(), + SYS_UNAME => self.sys_uname(args[0] as *mut u8), + SYS_UMASK => self.unimplemented("umask", Ok(0o777)), + // SYS_GETRLIMIT => self.sys_getrlimit(), + // SYS_SETRLIMIT => self.sys_setrlimit(), + SYS_GETRUSAGE => self.sys_getrusage(args[0], args[1] as *mut RUsage), + SYS_SYSINFO => self.sys_sysinfo(args[0] as *mut SysInfo), + SYS_TIMES => self.sys_times(args[0] as *mut Tms), + SYS_GETUID => self.unimplemented("getuid", Ok(0)), + SYS_GETGID => self.unimplemented("getgid", Ok(0)), + SYS_SETUID => self.unimplemented("setuid", Ok(0)), + SYS_GETEUID => self.unimplemented("geteuid", Ok(0)), + SYS_GETEGID => self.unimplemented("getegid", Ok(0)), + SYS_SETPGID => self.unimplemented("setpgid", Ok(0)), + SYS_GETPPID => self.sys_getppid(), + SYS_SETSID => self.unimplemented("setsid", Ok(0)), + SYS_GETPGID => self.unimplemented("getpgid", Ok(0)), + SYS_GETGROUPS => self.unimplemented("getgroups", Ok(0)), + SYS_SETGROUPS => self.unimplemented("setgroups", Ok(0)), + SYS_SETPRIORITY => self.sys_set_priority(args[0]), + SYS_PRCTL => self.unimplemented("prctl", Ok(0)), + SYS_PRLIMIT64 => self.sys_prlimit64( + args[0], + args[1], + args[2] as *const RLimit, + args[3] as *mut RLimit, + ), + SYS_REBOOT => self.sys_reboot( + args[0] as u32, + args[1] as u32, + args[2] as u32, + args[3] as *const u8, + ), + + // custom + SYS_MAP_PCI_DEVICE => self.sys_map_pci_device(args[0], args[1]), + SYS_GET_PADDR => { + self.sys_get_paddr(args[0] as *const u64, args[1] as *mut u64, args[2]) + } + //SYS_GETRANDOM => self.unimplemented("getrandom", Err(SysError::EINVAL)), + SYS_GETRANDOM => { + self.sys_getrandom(args[0] as *mut u8, args[1] as usize, args[2] as u32) + } + SYS_TKILL => self.unimplemented("tkill", Ok(0)), + _ => { + let ret = match () { + #[cfg(target_arch = "x86_64")] + () => self.x86_64_syscall(id, args), + #[cfg(target_arch = "mips")] + () => self.mips_syscall(id, args), + #[cfg(all(not(target_arch = "x86_64"), not(target_arch = "mips")))] + () => None, + }; + if let Some(ret) = ret { + ret + } else { + error!("unknown syscall id: {}, args: {:x?}", id, args); + crate::trap::error(self.tf); + } + } + }; + if !pid.is_init() { + // we trust pid 0 process + info!("=> {:x?}", ret); + } + #[cfg(feature = "profile")] + { + let end_time = unsafe { core::arch::x86_64::_rdtsc() }; + *SYSCALL_TIMING.lock().entry(id).or_insert(0) += end_time - begin_time; + if end_time % 1000 == 0 { + let timing = SYSCALL_TIMING.lock(); + let mut count_vec: Vec<(&usize, &i64)> = timing.iter().collect(); + count_vec.sort_by(|a, b| b.1.cmp(a.1)); + for (id, time) in count_vec.iter().take(5) { + warn!("timing {:03} time {:012}", id, time); } - Err(err) => Err(err), } } - SYS_GETPGID => { - warn!("sys_getpgid is unimplemented"); - Ok(0) + match ret { + Ok(code) => code as isize, + Err(err) => -(err as isize), } - SYS_SETPGID => { - warn!("sys_setpgid is unimplemented"); - Ok(0) - } - SYS_FCNTL64 => { - warn!("sys_fcntl64 is unimplemented"); - Ok(0) - } - SYS_SET_THREAD_AREA => { - info!("set_thread_area: tls: 0x{:x}", args[0]); - extern "C" { - fn _cur_tls(); - } + } - unsafe { - asm!("mtc0 $0, $$4, 2": :"r"(args[0])); - *(_cur_tls as *mut usize) = args[0]; - } - Ok(0) - } - _ => { - return None; - } - }; - Some(ret) -} + fn unimplemented(&self, name: &str, ret: SysResult) -> SysResult { + warn!("{} is unimplemented", name); + ret + } -#[cfg(target_arch = "x86_64")] -fn x86_64_syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> Option { - let ret = match id { - SYS_OPEN => sys_open(args[0] as *const u8, args[1], args[2]), - SYS_STAT => sys_stat(args[0] as *const u8, args[1] as *mut Stat), - SYS_LSTAT => sys_lstat(args[0] as *const u8, args[1] as *mut Stat), - SYS_POLL => sys_poll(args[0] as *mut PollFd, args[1], args[2]), - SYS_ACCESS => sys_access(args[0] as *const u8, args[1]), - SYS_PIPE => sys_pipe(args[0] as *mut u32), - SYS_SELECT => sys_select( - args[0], - args[1] as *mut u32, - args[2] as *mut u32, - args[3] as *mut u32, - args[4] as *const TimeVal, - ), - SYS_DUP2 => sys_dup2(args[0], args[1]), - SYS_ALARM => { - warn!("sys_alarm is unimplemented"); - Ok(0) - } - SYS_FORK => sys_fork(tf), - // use fork for vfork - SYS_VFORK => sys_fork(tf), - SYS_RENAME => sys_rename(args[0] as *const u8, args[1] as *const u8), - SYS_MKDIR => sys_mkdir(args[0] as *const u8, args[1]), - SYS_RMDIR => sys_rmdir(args[0] as *const u8), - SYS_LINK => sys_link(args[0] as *const u8, args[1] as *const u8), - SYS_UNLINK => sys_unlink(args[0] as *const u8), - SYS_READLINK => sys_readlink(args[0] as *const u8, args[1] as *mut u8, args[2]), - // 90 - SYS_CHMOD => { - warn!("sys_chmod is unimplemented"); - Ok(0) - } - SYS_CHOWN => { - warn!("sys_chown is unimplemented"); - Ok(0) - } - SYS_ARCH_PRCTL => sys_arch_prctl(args[0] as i32, args[1], tf), - SYS_TIME => sys_time(args[0] as *mut u64), - SYS_EPOLL_CREATE => { - warn!("sys_epoll_create is unimplemented"); - Err(SysError::ENOSYS) - } - _ => { - return None; - } - }; - Some(ret) + #[cfg(target_arch = "mips")] + fn mips_syscall(&mut self, id: usize, args: [usize; 6]) -> Option { + let ret = match id { + SYS_OPEN => self.sys_open(args[0] as *const u8, args[1], args[2]), + SYS_POLL => self.sys_poll(args[0] as *mut PollFd, args[1], args[2]), + SYS_DUP2 => self.sys_dup2(args[0], args[1]), + SYS_FORK => self.sys_fork(), + SYS_MMAP2 => self.sys_mmap(args[0], args[1], args[2], args[3], args[4], args[5] * 4096), + SYS_FSTAT64 => self.sys_fstat(args[0], args[1] as *mut Stat), + SYS_LSTAT64 => self.sys_lstat(args[0] as *const u8, args[1] as *mut Stat), + SYS_STAT64 => self.sys_stat(args[0] as *const u8, args[1] as *mut Stat), + SYS_PIPE => { + let fd_ptr = args[0] as *mut u32; + match self.sys_pipe(fd_ptr) { + Ok(code) => { + unsafe { + tf.v0 = *fd_ptr as usize; + tf.v1 = *(fd_ptr.add(1)) as usize; + } + Ok(tf.v0) + } + Err(err) => Err(err), + } + } + SYS_FCNTL64 => self.unimplemented("fcntl64", Ok(0)), + SYS_SET_THREAD_AREA => { + info!("set_thread_area: tls: 0x{:x}", args[0]); + extern "C" { + fn _cur_tls(); + } + + unsafe { + asm!("mtc0 $0, $$4, 2": :"r"(args[0])); + *(_cur_tls as *mut usize) = args[0]; + } + Ok(0) + } + _ => return None, + }; + Some(ret) + } + + #[cfg(target_arch = "x86_64")] + fn x86_64_syscall(&mut self, id: usize, args: [usize; 6]) -> Option { + let ret = match id { + SYS_OPEN => self.sys_open(args[0] as *const u8, args[1], args[2]), + SYS_STAT => self.sys_stat(args[0] as *const u8, args[1] as *mut Stat), + SYS_LSTAT => self.sys_lstat(args[0] as *const u8, args[1] as *mut Stat), + SYS_POLL => self.sys_poll(args[0] as *mut PollFd, args[1], args[2]), + SYS_ACCESS => self.sys_access(args[0] as *const u8, args[1]), + SYS_PIPE => self.sys_pipe(args[0] as *mut u32), + SYS_SELECT => self.sys_select( + args[0], + args[1] as *mut u32, + args[2] as *mut u32, + args[3] as *mut u32, + args[4] as *const TimeVal, + ), + SYS_DUP2 => self.sys_dup2(args[0], args[1]), + SYS_ALARM => self.unimplemented("alarm", Ok(0)), + SYS_FORK => self.sys_fork(), + SYS_VFORK => self.sys_vfork(), + SYS_RENAME => self.sys_rename(args[0] as *const u8, args[1] as *const u8), + SYS_MKDIR => self.sys_mkdir(args[0] as *const u8, args[1]), + SYS_RMDIR => self.sys_rmdir(args[0] as *const u8), + SYS_LINK => self.sys_link(args[0] as *const u8, args[1] as *const u8), + SYS_UNLINK => self.sys_unlink(args[0] as *const u8), + SYS_READLINK => self.sys_readlink(args[0] as *const u8, args[1] as *mut u8, args[2]), + SYS_CHMOD => self.unimplemented("chmod", Ok(0)), + SYS_CHOWN => self.unimplemented("chown", Ok(0)), + SYS_ARCH_PRCTL => self.sys_arch_prctl(args[0] as i32, args[1]), + SYS_TIME => self.sys_time(args[0] as *mut u64), + SYS_EPOLL_CREATE => self.unimplemented("epoll_create", Err(SysError::ENOSYS)), + _ => return None, + }; + Some(ret) + } } pub type SysResult = Result; diff --git a/kernel/src/syscall/net.rs b/kernel/src/syscall/net.rs index 13723408..6a1d176e 100644 --- a/kernel/src/syscall/net.rs +++ b/kernel/src/syscall/net.rs @@ -2,295 +2,300 @@ use super::fs::IoVecs; use super::*; -use crate::drivers::SOCKET_ACTIVITY; use crate::fs::FileLike; +use crate::memory::MemorySet; use crate::net::{ Endpoint, LinkLevelEndpoint, NetlinkEndpoint, NetlinkSocketState, PacketSocketState, RawSocketState, Socket, TcpSocketState, UdpSocketState, SOCKETS, }; -use crate::sync::{MutexGuard, SpinNoIrq, SpinNoIrqLock as Mutex}; use alloc::boxed::Box; use core::cmp::min; use core::mem::size_of; use smoltcp::wire::*; -pub fn sys_socket(domain: usize, socket_type: usize, protocol: usize) -> SysResult { - let domain = AddressFamily::from(domain as u16); - let socket_type = SocketType::from(socket_type as u8 & SOCK_TYPE_MASK); - info!( - "socket: domain: {:?}, socket_type: {:?}, protocol: {}", - domain, socket_type, protocol - ); - let mut proc = process(); - let socket: Box = match domain { - AddressFamily::Internet | AddressFamily::Unix => match socket_type { - SocketType::Stream => Box::new(TcpSocketState::new()), - SocketType::Datagram => Box::new(UdpSocketState::new()), - SocketType::Raw => Box::new(RawSocketState::new(protocol as u8)), - _ => return Err(SysError::EINVAL), - }, - AddressFamily::Packet => match socket_type { - SocketType::Raw => Box::new(PacketSocketState::new()), - _ => return Err(SysError::EINVAL), - }, - AddressFamily::Netlink => match socket_type { - SocketType::Raw => Box::new(NetlinkSocketState::new()), - _ => return Err(SysError::EINVAL), - }, - _ => return Err(SysError::EAFNOSUPPORT), - }; - let fd = proc.get_free_fd(); - proc.files.insert(fd, FileLike::Socket(socket)); - Ok(fd) -} - -pub fn sys_setsockopt( - fd: usize, - level: usize, - optname: usize, - optval: *const u8, - optlen: usize, -) -> SysResult { - info!( - "setsockopt: fd: {}, level: {}, optname: {}", - fd, level, optname - ); - let mut proc = process(); - proc.vm.check_read_array(optval, optlen)?; - let data = unsafe { slice::from_raw_parts(optval, optlen) }; - let socket = proc.get_socket(fd)?; - socket.setsockopt(level, optname, data) -} - -pub fn sys_getsockopt( - fd: usize, - level: usize, - optname: usize, - optval: *mut u8, - optlen: *mut u32, -) -> SysResult { - info!( - "getsockopt: fd: {}, level: {}, optname: {} optval: {:?} optlen: {:?}", - fd, level, optname, optval, optlen - ); - let proc = process(); - proc.vm.check_write_ptr(optlen)?; - match level { - SOL_SOCKET => match optname { - SO_SNDBUF => { - proc.vm.check_write_array(optval, 4)?; - unsafe { - *(optval as *mut u32) = crate::net::TCP_SENDBUF as u32; - *optlen = 4; - } - Ok(0) - } - SO_RCVBUF => { - proc.vm.check_write_array(optval, 4)?; - unsafe { - *(optval as *mut u32) = crate::net::TCP_RECVBUF as u32; - *optlen = 4; - } - Ok(0) - } - _ => Err(SysError::ENOPROTOOPT), - }, - IPPROTO_TCP => match optname { - TCP_CONGESTION => Ok(0), - _ => Err(SysError::ENOPROTOOPT), - }, - _ => Err(SysError::ENOPROTOOPT), +impl Syscall<'_> { + pub fn sys_socket(&mut self, domain: usize, socket_type: usize, protocol: usize) -> SysResult { + let domain = AddressFamily::from(domain as u16); + let socket_type = SocketType::from(socket_type as u8 & SOCK_TYPE_MASK); + info!( + "socket: domain: {:?}, socket_type: {:?}, protocol: {}", + domain, socket_type, protocol + ); + let mut proc = self.process(); + let socket: Box = match domain { + AddressFamily::Internet | AddressFamily::Unix => match socket_type { + SocketType::Stream => Box::new(TcpSocketState::new()), + SocketType::Datagram => Box::new(UdpSocketState::new()), + SocketType::Raw => Box::new(RawSocketState::new(protocol as u8)), + _ => return Err(SysError::EINVAL), + }, + AddressFamily::Packet => match socket_type { + SocketType::Raw => Box::new(PacketSocketState::new()), + _ => return Err(SysError::EINVAL), + }, + AddressFamily::Netlink => match socket_type { + SocketType::Raw => Box::new(NetlinkSocketState::new()), + _ => return Err(SysError::EINVAL), + }, + _ => return Err(SysError::EAFNOSUPPORT), + }; + let fd = proc.add_file(FileLike::Socket(socket)); + Ok(fd) } -} -pub fn sys_connect(fd: usize, addr: *const SockAddr, addr_len: usize) -> SysResult { - info!( - "sys_connect: fd: {}, addr: {:?}, addr_len: {}", - fd, addr, addr_len - ); + pub fn sys_setsockopt( + &mut self, + fd: usize, + level: usize, + optname: usize, + optval: *const u8, + optlen: usize, + ) -> SysResult { + info!( + "setsockopt: fd: {}, level: {}, optname: {}", + fd, level, optname + ); + let mut proc = self.process(); + let data = unsafe { self.vm().check_read_array(optval, optlen)? }; + let socket = proc.get_socket(fd)?; + socket.setsockopt(level, optname, data) + } - let mut proc = process(); - let endpoint = sockaddr_to_endpoint(&mut proc, addr, addr_len)?; - let socket = proc.get_socket(fd)?; - socket.connect(endpoint)?; - Ok(0) -} - -pub fn sys_sendto( - fd: usize, - base: *const u8, - len: usize, - _flags: usize, - addr: *const SockAddr, - addr_len: usize, -) -> SysResult { - info!( - "sys_sendto: fd: {} base: {:?} len: {} addr: {:?} addr_len: {}", - fd, base, len, addr, addr_len - ); - - let mut proc = process(); - proc.vm.check_read_array(base, len)?; - - let slice = unsafe { slice::from_raw_parts(base, len) }; - let endpoint = if addr.is_null() { - None - } else { - let endpoint = sockaddr_to_endpoint(&mut proc, addr, addr_len)?; - info!("sys_sendto: sending to endpoint {:?}", endpoint); - Some(endpoint) - }; - let socket = proc.get_socket(fd)?; - socket.write(&slice, endpoint) -} - -pub fn sys_recvfrom( - fd: usize, - base: *mut u8, - len: usize, - flags: usize, - addr: *mut SockAddr, - addr_len: *mut u32, -) -> SysResult { - info!( - "sys_recvfrom: fd: {} base: {:?} len: {} flags: {} addr: {:?} addr_len: {:?}", - fd, base, len, flags, addr, addr_len - ); - - let mut proc = process(); - proc.vm.check_write_array(base, len)?; - - let socket = proc.get_socket(fd)?; - let mut slice = unsafe { slice::from_raw_parts_mut(base, len) }; - let (result, endpoint) = socket.read(&mut slice); - - if result.is_ok() && !addr.is_null() { - let sockaddr_in = SockAddr::from(endpoint); - unsafe { - sockaddr_in.write_to(&mut proc, addr, addr_len)?; + pub fn sys_getsockopt( + &mut self, + fd: usize, + level: usize, + optname: usize, + optval: *mut u8, + optlen: *mut u32, + ) -> SysResult { + info!( + "getsockopt: fd: {}, level: {}, optname: {} optval: {:?} optlen: {:?}", + fd, level, optname, optval, optlen + ); + let optlen = unsafe { self.vm().check_write_ptr(optlen)? }; + match level { + SOL_SOCKET => match optname { + SO_SNDBUF => { + let optval = unsafe { self.vm().check_write_ptr(optval as *mut u32)? }; + *optval = crate::net::TCP_SENDBUF as u32; + *optlen = 4; + Ok(0) + } + SO_RCVBUF => { + let optval = unsafe { self.vm().check_write_ptr(optval as *mut u32)? }; + *optval = crate::net::TCP_RECVBUF as u32; + *optlen = 4; + Ok(0) + } + _ => Err(SysError::ENOPROTOOPT), + }, + IPPROTO_TCP => match optname { + TCP_CONGESTION => Ok(0), + _ => Err(SysError::ENOPROTOOPT), + }, + _ => Err(SysError::ENOPROTOOPT), } } - result -} + pub fn sys_connect(&mut self, fd: usize, addr: *const SockAddr, addr_len: usize) -> SysResult { + info!( + "sys_connect: fd: {}, addr: {:?}, addr_len: {}", + fd, addr, addr_len + ); -pub fn sys_recvmsg(fd: usize, msg: *mut MsgHdr, flags: usize) -> SysResult { - info!("recvmsg: fd: {}, msg: {:?}, flags: {}", fd, msg, flags); - let mut proc = process(); - proc.vm.check_read_ptr(msg)?; - let hdr = unsafe { &mut *msg }; - let mut iovs = IoVecs::check_and_new(hdr.msg_iov, hdr.msg_iovlen, &proc.vm, true)?; + let mut proc = self.process(); + let endpoint = sockaddr_to_endpoint(&mut self.vm(), addr, addr_len)?; + let socket = proc.get_socket(fd)?; + socket.connect(endpoint)?; + Ok(0) + } - let mut buf = iovs.new_buf(true); - let socket = proc.get_socket(fd)?; - let (result, endpoint) = socket.read(&mut buf); + pub fn sys_sendto( + &mut self, + fd: usize, + base: *const u8, + len: usize, + _flags: usize, + addr: *const SockAddr, + addr_len: usize, + ) -> SysResult { + info!( + "sys_sendto: fd: {} base: {:?} len: {} addr: {:?} addr_len: {}", + fd, base, len, addr, addr_len + ); - if let Ok(len) = result { - // copy data to user - iovs.write_all_from_slice(&buf[..len]); + let mut proc = self.process(); + + let slice = unsafe { self.vm().check_read_array(base, len)? }; + let endpoint = if addr.is_null() { + None + } else { + let endpoint = sockaddr_to_endpoint(&mut self.vm(), addr, addr_len)?; + info!("sys_sendto: sending to endpoint {:?}", endpoint); + Some(endpoint) + }; + let socket = proc.get_socket(fd)?; + socket.write(&slice, endpoint) + } + + pub fn sys_recvfrom( + &mut self, + fd: usize, + base: *mut u8, + len: usize, + flags: usize, + addr: *mut SockAddr, + addr_len: *mut u32, + ) -> SysResult { + info!( + "sys_recvfrom: fd: {} base: {:?} len: {} flags: {} addr: {:?} addr_len: {:?}", + fd, base, len, flags, addr, addr_len + ); + + let mut proc = self.process(); + + let mut slice = unsafe { self.vm().check_write_array(base, len)? }; + let socket = proc.get_socket(fd)?; + let (result, endpoint) = socket.read(&mut slice); + + if result.is_ok() && !addr.is_null() { + let sockaddr_in = SockAddr::from(endpoint); + unsafe { + sockaddr_in.write_to(&mut self.vm(), addr, addr_len)?; + } + } + + result + } + + pub fn sys_recvmsg(&mut self, fd: usize, msg: *mut MsgHdr, flags: usize) -> SysResult { + info!("recvmsg: fd: {}, msg: {:?}, flags: {}", fd, msg, flags); + let mut proc = self.process(); + let hdr = unsafe { self.vm().check_write_ptr(msg)? }; + let mut iovs = + unsafe { IoVecs::check_and_new(hdr.msg_iov, hdr.msg_iovlen, &self.vm(), true)? }; + + let mut buf = iovs.new_buf(true); + let socket = proc.get_socket(fd)?; + let (result, endpoint) = socket.read(&mut buf); + + if let Ok(len) = result { + // copy data to user + iovs.write_all_from_slice(&buf[..len]); + let sockaddr_in = SockAddr::from(endpoint); + unsafe { + sockaddr_in.write_to(&mut self.vm(), hdr.msg_name, &mut hdr.msg_namelen as *mut u32)?; + } + } + result + } + + pub fn sys_bind(&mut self, fd: usize, addr: *const SockAddr, addr_len: usize) -> SysResult { + info!("sys_bind: fd: {} addr: {:?} len: {}", fd, addr, addr_len); + let mut proc = self.process(); + + let endpoint = sockaddr_to_endpoint(&mut self.vm(), addr, addr_len)?; + info!("sys_bind: fd: {} bind to {:?}", fd, endpoint); + + let socket = proc.get_socket(fd)?; + socket.bind(endpoint) + } + + pub fn sys_listen(&mut self, fd: usize, backlog: usize) -> SysResult { + info!("sys_listen: fd: {} backlog: {}", fd, backlog); + // smoltcp tcp sockets do not support backlog + // open multiple sockets for each connection + let mut proc = self.process(); + + let socket = proc.get_socket(fd)?; + socket.listen() + } + + pub fn sys_shutdown(&mut self, fd: usize, how: usize) -> SysResult { + info!("sys_shutdown: fd: {} how: {}", fd, how); + let mut proc = self.process(); + + let socket = proc.get_socket(fd)?; + socket.shutdown() + } + + pub fn sys_accept(&mut self, fd: usize, addr: *mut SockAddr, addr_len: *mut u32) -> SysResult { + info!( + "sys_accept: fd: {} addr: {:?} addr_len: {:?}", + fd, addr, addr_len + ); + // smoltcp tcp sockets do not support backlog + // open multiple sockets for each connection + let mut proc = self.process(); + + let socket = proc.get_socket(fd)?; + let (new_socket, remote_endpoint) = socket.accept()?; + + let new_fd = proc.add_file(FileLike::Socket(new_socket)); + + if !addr.is_null() { + let sockaddr_in = SockAddr::from(remote_endpoint); + unsafe { + sockaddr_in.write_to(&mut self.vm(), addr, addr_len)?; + } + } + Ok(new_fd) + } + + pub fn sys_getsockname( + &mut self, + fd: usize, + addr: *mut SockAddr, + addr_len: *mut u32, + ) -> SysResult { + info!( + "sys_getsockname: fd: {} addr: {:?} addr_len: {:?}", + fd, addr, addr_len + ); + + let mut proc = self.process(); + + if addr.is_null() { + return Err(SysError::EINVAL); + } + + let socket = proc.get_socket(fd)?; + let endpoint = socket.endpoint().ok_or(SysError::EINVAL)?; let sockaddr_in = SockAddr::from(endpoint); unsafe { - sockaddr_in.write_to(&mut proc, hdr.msg_name, &mut hdr.msg_namelen as *mut u32)?; + sockaddr_in.write_to(&mut self.vm(), addr, addr_len)?; } + Ok(0) } - result -} -pub fn sys_bind(fd: usize, addr: *const SockAddr, addr_len: usize) -> SysResult { - info!("sys_bind: fd: {} addr: {:?} len: {}", fd, addr, addr_len); - let mut proc = process(); + pub fn sys_getpeername( + &mut self, + fd: usize, + addr: *mut SockAddr, + addr_len: *mut u32, + ) -> SysResult { + info!( + "sys_getpeername: fd: {} addr: {:?} addr_len: {:?}", + fd, addr, addr_len + ); - let mut endpoint = sockaddr_to_endpoint(&mut proc, addr, addr_len)?; - info!("sys_bind: fd: {} bind to {:?}", fd, endpoint); + // smoltcp tcp sockets do not support backlog + // open multiple sockets for each connection + let mut proc = self.process(); - let socket = proc.get_socket(fd)?; - socket.bind(endpoint) -} + if addr as usize == 0 { + return Err(SysError::EINVAL); + } -pub fn sys_listen(fd: usize, backlog: usize) -> SysResult { - info!("sys_listen: fd: {} backlog: {}", fd, backlog); - // smoltcp tcp sockets do not support backlog - // open multiple sockets for each connection - let mut proc = process(); - - let socket = proc.get_socket(fd)?; - socket.listen() -} - -pub fn sys_shutdown(fd: usize, how: usize) -> SysResult { - info!("sys_shutdown: fd: {} how: {}", fd, how); - let mut proc = process(); - - let socket = proc.get_socket(fd)?; - socket.shutdown() -} - -pub fn sys_accept(fd: usize, addr: *mut SockAddr, addr_len: *mut u32) -> SysResult { - info!( - "sys_accept: fd: {} addr: {:?} addr_len: {:?}", - fd, addr, addr_len - ); - // smoltcp tcp sockets do not support backlog - // open multiple sockets for each connection - let mut proc = process(); - - let socket = proc.get_socket(fd)?; - let (new_socket, remote_endpoint) = socket.accept()?; - - let new_fd = proc.get_free_fd(); - proc.files.insert(new_fd, FileLike::Socket(new_socket)); - - if !addr.is_null() { + let socket = proc.get_socket(fd)?; + let remote_endpoint = socket.remote_endpoint().ok_or(SysError::EINVAL)?; let sockaddr_in = SockAddr::from(remote_endpoint); unsafe { - sockaddr_in.write_to(&mut proc, addr, addr_len)?; + sockaddr_in.write_to(&mut self.vm(), addr, addr_len)?; } + Ok(0) } - Ok(new_fd) -} - -pub fn sys_getsockname(fd: usize, addr: *mut SockAddr, addr_len: *mut u32) -> SysResult { - info!( - "sys_getsockname: fd: {} addr: {:?} addr_len: {:?}", - fd, addr, addr_len - ); - - let mut proc = process(); - - if addr.is_null() { - return Err(SysError::EINVAL); - } - - let socket = proc.get_socket(fd)?; - let endpoint = socket.endpoint().ok_or(SysError::EINVAL)?; - let sockaddr_in = SockAddr::from(endpoint); - unsafe { - sockaddr_in.write_to(&mut proc, addr, addr_len)?; - } - Ok(0) -} - -pub fn sys_getpeername(fd: usize, addr: *mut SockAddr, addr_len: *mut u32) -> SysResult { - info!( - "sys_getpeername: fd: {} addr: {:?} addr_len: {:?}", - fd, addr, addr_len - ); - - // smoltcp tcp sockets do not support backlog - // open multiple sockets for each connection - let mut proc = process(); - - if addr as usize == 0 { - return Err(SysError::EINVAL); - } - - let socket = proc.get_socket(fd)?; - let remote_endpoint = socket.remote_endpoint().ok_or(SysError::EINVAL)?; - let sockaddr_in = SockAddr::from(remote_endpoint); - unsafe { - sockaddr_in.write_to(&mut proc, addr, addr_len)?; - } - Ok(0) } impl Process { @@ -401,42 +406,36 @@ impl From for SockAddr { /// Convert sockaddr to endpoint // Check len is long enough fn sockaddr_to_endpoint( - proc: &mut Process, + vm: &MemorySet, addr: *const SockAddr, len: usize, ) -> Result { if len < size_of::() { return Err(SysError::EINVAL); } - proc.vm.check_read_array(addr as *const u8, len)?; + let addr = unsafe { vm.check_read_ptr(addr)? }; + if len < addr.len()? { + return Err(SysError::EINVAL); + } unsafe { - match AddressFamily::from((*addr).family) { + match AddressFamily::from(addr.family) { AddressFamily::Internet => { - if len < size_of::() { - return Err(SysError::EINVAL); - } - let port = u16::from_be((*addr).addr_in.sin_port); + let port = u16::from_be(addr.addr_in.sin_port); let addr = IpAddress::from(Ipv4Address::from_bytes( - &u32::from_be((*addr).addr_in.sin_addr).to_be_bytes()[..], + &u32::from_be(addr.addr_in.sin_addr).to_be_bytes()[..], )); Ok(Endpoint::Ip((addr, port).into())) } AddressFamily::Unix => Err(SysError::EINVAL), AddressFamily::Packet => { - if len < size_of::() { - return Err(SysError::EINVAL); - } Ok(Endpoint::LinkLevel(LinkLevelEndpoint::new( - (*addr).addr_ll.sll_ifindex as usize, + addr.addr_ll.sll_ifindex as usize, ))) } AddressFamily::Netlink => { - if len < size_of::() { - return Err(SysError::EINVAL); - } Ok(Endpoint::Netlink(NetlinkEndpoint::new( - (*addr).addr_nl.nl_pid, - (*addr).addr_nl.nl_groups, + addr.addr_nl.nl_pid, + addr.addr_nl.nl_groups, ))) } _ => Err(SysError::EINVAL), @@ -445,11 +444,21 @@ fn sockaddr_to_endpoint( } impl SockAddr { + fn len(&self) -> Result { + match AddressFamily::from(unsafe { self.family }) { + AddressFamily::Internet => Ok(size_of::()), + AddressFamily::Packet => Ok(size_of::()), + AddressFamily::Netlink => Ok(size_of::()), + AddressFamily::Unix => Err(SysError::EINVAL), + _ => Err(SysError::EINVAL), + } + } + /// Write to user sockaddr /// Check mutability for user unsafe fn write_to( self, - proc: &mut Process, + vm: &MemorySet, addr: *mut SockAddr, addr_len: *mut u32, ) -> SysResult { @@ -458,24 +467,17 @@ impl SockAddr { return Ok(0); } - proc.vm.check_write_ptr(addr_len)?; + let addr_len = unsafe { vm.check_write_ptr(addr_len)? }; let max_addr_len = *addr_len as usize; - let full_len = match AddressFamily::from(self.family) { - AddressFamily::Internet => size_of::(), - AddressFamily::Packet => size_of::(), - AddressFamily::Netlink => size_of::(), - AddressFamily::Unix => return Err(SysError::EINVAL), - _ => return Err(SysError::EINVAL), - }; + let full_len = self.len()?; let written_len = min(max_addr_len, full_len); if written_len > 0 { - proc.vm.check_write_array(addr as *mut u8, written_len)?; + let target = unsafe { vm.check_write_array(addr as *mut u8, written_len)? }; let source = slice::from_raw_parts(&self as *const SockAddr as *const u8, written_len); - let target = slice::from_raw_parts_mut(addr as *mut u8, written_len); target.copy_from_slice(source); } - addr_len.write(full_len as u32); + *addr_len = full_len as u32; return Ok(0); } } diff --git a/kernel/src/syscall/proc.rs b/kernel/src/syscall/proc.rs index c6c6b1fe..fbdbc2d7 100644 --- a/kernel/src/syscall/proc.rs +++ b/kernel/src/syscall/proc.rs @@ -3,343 +3,349 @@ use super::*; use crate::fs::INodeExt; -/// Fork the current process. Return the child's PID. -pub fn sys_fork(tf: &TrapFrame) -> SysResult { - let new_thread = current_thread().fork(tf); - let pid = processor().manager().add(new_thread); - info!("fork: {} -> {}", thread::current().id(), pid); - Ok(pid) -} +impl Syscall<'_> { + /// Fork the current process. Return the child's PID. + pub fn sys_fork(&mut self) -> SysResult { + let new_thread = self.thread.fork(self.tf); + let pid = new_thread.proc.lock().pid.get(); + let tid = processor().manager().add(new_thread); + processor().manager().detach(tid); + info!("fork: {} -> {}", thread::current().id(), pid); + Ok(pid) + } -/// Create a new thread in the current process. -/// The new thread's stack pointer will be set to `newsp`, -/// and thread pointer will be set to `newtls`. -/// The child tid will be stored at both `parent_tid` and `child_tid`. -/// This is partially implemented for musl only. -pub fn sys_clone( - flags: usize, - newsp: usize, - parent_tid: *mut u32, - child_tid: *mut u32, - newtls: usize, - tf: &TrapFrame, -) -> SysResult { - let clone_flags = CloneFlags::from_bits_truncate(flags); - info!( - "clone: flags: {:?} == {:#x}, newsp: {:#x}, parent_tid: {:?}, child_tid: {:?}, newtls: {:#x}", - clone_flags, flags, newsp, parent_tid, child_tid, newtls - ); - if flags == 0x4111 || flags == 0x11 { - warn!("sys_clone is calling sys_fork instead, ignoring other args"); - return sys_fork(tf); + pub fn sys_vfork(&mut self) -> SysResult { + self.sys_fork() } - if (flags != 0x7d0f00) && (flags != 0x5d0f00) { - //0x5d0f00 is the args from gcc of alpine linux - //warn!("sys_clone only support musl pthread_create"); - panic!( - "sys_clone only support sys_fork OR musl pthread_create without flags{:x}", - flags - ); - //return Err(SysError::ENOSYS); - } - { - let proc = process(); - proc.vm.check_write_ptr(parent_tid)?; - proc.vm.check_write_ptr(child_tid)?; - } - let new_thread = current_thread().clone(tf, newsp, newtls, child_tid as usize); - // FIXME: parent pid - let tid = processor().manager().add(new_thread); - info!("clone: {} -> {}", thread::current().id(), tid); - unsafe { - parent_tid.write(tid as u32); - child_tid.write(tid as u32); - } - Ok(tid) -} -/// Wait for the process exit. -/// Return the PID. Store exit code to `wstatus` if it's not null. -pub fn sys_wait4(pid: isize, wstatus: *mut i32) -> SysResult { - info!("wait4: pid: {}, code: {:?}", pid, wstatus); - if !wstatus.is_null() { - process().vm.check_write_ptr(wstatus)?; - } - #[derive(Debug)] - enum WaitFor { - AnyChild, - Pid(usize), - } - let target = match pid { - -1 | 0 => WaitFor::AnyChild, - p if p > 0 => WaitFor::Pid(p as usize), - _ => unimplemented!(), - }; - loop { - let mut proc = process(); - // check child_exit_code - let find = match target { - WaitFor::AnyChild => proc - .child_exit_code - .iter() - .next() - .map(|(&pid, &code)| (pid, code)), - WaitFor::Pid(pid) => proc.child_exit_code.get(&pid).map(|&code| (pid, code)), - }; - // if found, return - if let Some((pid, exit_code)) = find { - proc.child_exit_code.remove(&pid); - if !wstatus.is_null() { - unsafe { - wstatus.write(exit_code as i32); - } - } - return Ok(pid); - } - // if not, check pid - let children: Vec<_> = proc - .children - .iter() - .filter_map(|weak| weak.upgrade()) - .collect(); - let invalid = match target { - WaitFor::AnyChild => children.len() == 0, - WaitFor::Pid(pid) => children - .iter() - .find(|p| p.lock().pid.get() == pid) - .is_none(), - }; - if invalid { - return Err(SysError::ECHILD); - } + /// Create a new thread in the current process. + /// The new thread's stack pointer will be set to `newsp`, + /// and thread pointer will be set to `newtls`. + /// The child tid will be stored at both `parent_tid` and `child_tid`. + /// This is partially implemented for musl only. + pub fn sys_clone( + &mut self, + flags: usize, + newsp: usize, + parent_tid: *mut u32, + child_tid: *mut u32, + newtls: usize, + ) -> SysResult { + let clone_flags = CloneFlags::from_bits_truncate(flags); info!( - "wait: thread {} -> {:?}, sleep", - thread::current().id(), - target + "clone: flags: {:?} == {:#x}, newsp: {:#x}, parent_tid: {:?}, child_tid: {:?}, newtls: {:#x}", + clone_flags, flags, newsp, parent_tid, child_tid, newtls ); - let condvar = proc.child_exit.clone(); - drop(proc); // must release lock of current process - condvar._wait(); - } -} - -pub fn sys_exec( - name: *const u8, - argv: *const *const u8, - envp: *const *const u8, - tf: &mut TrapFrame, -) -> SysResult { - info!("exec: name: {:?}, argv: {:?}, envp: {:?}", name, argv, envp); - let proc = process(); - let exec_name = if name.is_null() { - String::from("") - } else { - unsafe { proc.vm.check_and_clone_cstr(name)? } - }; - - if argv.is_null() { - return Err(SysError::EINVAL); - } - // Check and copy args to kernel - let mut args = Vec::new(); - unsafe { - let mut current_argv = argv as *const *const u8; - proc.vm.check_read_ptr(current_argv)?; - while !(*current_argv).is_null() { - let arg = proc.vm.check_and_clone_cstr(*current_argv)?; - args.push(arg); - current_argv = current_argv.add(1); + if flags == 0x4111 || flags == 0x11 { + warn!("sys_clone is calling sys_fork instead, ignoring other args"); + return self.sys_fork(); } - } - // Check and copy envs to kernel - let mut envs = Vec::new(); - unsafe { - let mut current_env = envp as *const *const u8; - if !current_env.is_null() { - proc.vm.check_read_ptr(current_env)?; - while !(*current_env).is_null() { - let env = proc.vm.check_and_clone_cstr(*current_env)?; - envs.push(env); - current_env = current_env.add(1); - } + if (flags != 0x7d0f00) && (flags != 0x5d0f00) { + //0x5d0f00 is the args from gcc of alpine linux + //warn!("sys_clone only support musl pthread_create"); + panic!( + "sys_clone only support sys_fork OR musl pthread_create without flags{:x}", + flags + ); + //return Err(SysError::ENOSYS); } + let parent_tid_ref = unsafe { self.vm().check_write_ptr(parent_tid)? }; + let child_tid_ref = unsafe { self.vm().check_write_ptr(child_tid)? }; + let new_thread = self + .thread + .clone(self.tf, newsp, newtls, child_tid as usize); + // FIXME: parent pid + let tid = processor().manager().add(new_thread); + processor().manager().detach(tid); + info!("clone: {} -> {}", thread::current().id(), tid); + *parent_tid_ref = tid as u32; + *child_tid_ref = tid as u32; + Ok(tid) } - if args.is_empty() { - return Err(SysError::EINVAL); - } - - info!( - "exec: name: {:?}, args: {:?}, envp: {:?}", - exec_name, args, envs - ); - - // Read program file - //let path = args[0].as_str(); - let exec_path = exec_name.as_str(); - let inode = proc.lookup_inode(exec_path)?; - let buf = inode.read_as_vec()?; - - // Make new Thread - let mut thread = Thread::new_user(buf.as_slice(), exec_path, args, envs); - thread.proc.lock().clone_for_exec(&proc); - - // Activate new page table - unsafe { - thread.proc.lock().vm.activate(); - } - - // Modify the TrapFrame - *tf = unsafe { thread.context.get_init_tf() }; - - // Swap Context but keep KStack - ::core::mem::swap(&mut current_thread().kstack, &mut thread.kstack); - ::core::mem::swap(current_thread(), &mut *thread); - - Ok(0) -} - -pub fn sys_yield() -> SysResult { - thread::yield_now(); - Ok(0) -} - -/// Kill the process -pub fn sys_kill(pid: usize, sig: usize) -> SysResult { - info!( - "kill: {} killed: {} with sig {}", - thread::current().id(), - pid, - sig - ); - let current_pid = process().pid.get().clone(); - if current_pid == pid { - // killing myself - sys_exit_group(sig); - } else { - if let Some(proc_arc) = PROCESSES.read().get(&pid).and_then(|weak| weak.upgrade()) { - let proc = proc_arc.lock(); - // quit all threads - for tid in proc.threads.iter() { - processor().manager().exit(*tid, sig); - } - // notify parent and fill exit code - // avoid deadlock - let proc_parent = proc.parent.clone(); - let pid = proc.pid.get(); - drop(proc); - if let Some(parent) = proc_parent { - let mut parent = parent.lock(); - parent.child_exit_code.insert(pid, sig); - parent.child_exit.notify_one(); - } - Ok(0) + /// Wait for the process exit. + /// Return the PID. Store exit code to `wstatus` if it's not null. + pub fn sys_wait4(&mut self, pid: isize, wstatus: *mut i32) -> SysResult { + //info!("wait4: pid: {}, code: {:?}", pid, wstatus); + let wstatus = if !wstatus.is_null() { + Some(unsafe { self.vm().check_write_ptr(wstatus)? }) } else { - Err(SysError::EINVAL) + None + }; + #[derive(Debug)] + enum WaitFor { + AnyChild, + Pid(usize), + } + let target = match pid { + -1 | 0 => WaitFor::AnyChild, + p if p > 0 => WaitFor::Pid(p as usize), + _ => unimplemented!(), + }; + loop { + let mut proc = self.process(); + // check child_exit_code + let find = match target { + WaitFor::AnyChild => proc + .child_exit_code + .iter() + .next() + .map(|(&pid, &code)| (pid, code)), + WaitFor::Pid(pid) => proc.child_exit_code.get(&pid).map(|&code| (pid, code)), + }; + // if found, return + if let Some((pid, exit_code)) = find { + proc.child_exit_code.remove(&pid); + if let Some(wstatus) = wstatus { + *wstatus = exit_code as i32; + } + return Ok(pid); + } + // if not, check pid + let children: Vec<_> = proc + .children + .iter() + .filter_map(|weak| weak.upgrade()) + .collect(); + let invalid = match target { + WaitFor::AnyChild => children.len() == 0, + WaitFor::Pid(pid) => children + .iter() + .find(|p| p.lock().pid.get() == pid) + .is_none(), + }; + if invalid { + return Err(SysError::ECHILD); + } + info!( + "wait: thread {} -> {:?}, sleep", + thread::current().id(), + target + ); + let condvar = proc.child_exit.clone(); + condvar.wait(proc); } } -} -/// Get the current process id -pub fn sys_getpid() -> SysResult { - info!("getpid"); - Ok(process().pid.get()) -} + /// Replaces the current ** process ** with a new process image + /// + /// `argv` is an array of argument strings passed to the new program. + /// `envp` is an array of strings, conventionally of the form `key=value`, + /// which are passed as environment to the new program. + /// + /// NOTICE: `argv` & `envp` can not be NULL (different from Linux) + /// + /// NOTICE: for multi-thread programs + /// A call to any exec function from a process with more than one thread + /// shall result in all threads being terminated and the new executable image + /// being loaded and executed. + pub fn sys_exec( + &mut self, + path: *const u8, + argv: *const *const u8, + envp: *const *const u8, + ) -> SysResult { + info!( + "exec:BEG: path: {:?}, argv: {:?}, envp: {:?}", + path, argv, envp + ); + let mut proc = self.process(); + let path = unsafe { self.vm().check_and_clone_cstr(path)? }; + let args = unsafe { self.vm().check_and_clone_cstr_array(argv)? }; + let envs = unsafe { self.vm().check_and_clone_cstr_array(envp)? }; -/// Get the current thread id -pub fn sys_gettid() -> SysResult { - info!("gettid"); - // use pid as tid for now - Ok(thread::current().id()) -} + if args.is_empty() { + error!("exec: args is null"); + return Err(SysError::EINVAL); + } -/// Get the parent process id -pub fn sys_getppid() -> SysResult { - if let Some(ref parent) = process().parent.as_ref() { - Ok(parent.lock().pid.get()) - } else { + info!( + "exec:STEP2: path: {:?}, args: {:?}, envs: {:?}", + path, args, envs + ); + + // Kill other threads + proc.threads.retain(|&tid| { + if tid != processor().tid() { + processor().manager().exit(tid, 1); + } + tid == processor().tid() + }); + + // Read program file + let inode = proc.lookup_inode(&path)?; + + // Make new Thread + let (mut vm, entry_addr, ustack_top) = + Thread::new_user_vm(&inode, &path, args, envs).map_err(|_| SysError::EINVAL)?; + + // Activate new page table + core::mem::swap(&mut *self.vm(), &mut vm); + unsafe { + self.vm().activate(); + } + + // Modify exec path + proc.exec_path = path.clone(); + drop(proc); + + // Modify the TrapFrame + *self.tf = TrapFrame::new_user_thread(entry_addr, ustack_top); + + info!("exec:END: path: {:?}", path); Ok(0) } -} -/// Exit the current thread -pub fn sys_exit(exit_code: usize) -> ! { - let tid = thread::current().id(); - info!("exit: {}, code: {}", tid, exit_code); - let mut proc = process(); - proc.threads.retain(|&id| id != tid); + pub fn sys_yield(&mut self) -> SysResult { + thread::yield_now(); + Ok(0) + } - // for last thread, - // notify parent and fill exit code - // avoid deadlock - let exit = proc.threads.len() == 0; - let proc_parent = proc.parent.clone(); - let pid = proc.pid.get(); - drop(proc); - if exit { + /// Kill the process + pub fn sys_kill(&mut self, pid: usize, sig: usize) -> SysResult { + info!( + "kill: {} killed: {} with sig {}", + thread::current().id(), + pid, + sig + ); + let current_pid = self.process().pid.get().clone(); + if current_pid == pid { + // killing myself + self.sys_exit_group(sig); + } else { + if let Some(proc_arc) = PROCESSES.read().get(&pid).and_then(|weak| weak.upgrade()) { + let proc = proc_arc.lock(); + // quit all threads + for tid in proc.threads.iter() { + processor().manager().exit(*tid, sig); + } + // notify parent and fill exit code + // avoid deadlock + let proc_parent = proc.parent.clone(); + let pid = proc.pid.get(); + drop(proc); + if let Some(parent) = proc_parent { + let mut parent = parent.lock(); + parent.child_exit_code.insert(pid, sig); + parent.child_exit.notify_one(); + } + Ok(0) + } else { + Err(SysError::EINVAL) + } + } + } + + /// Get the current process id + pub fn sys_getpid(&mut self) -> SysResult { + info!("getpid"); + Ok(self.process().pid.get()) + } + + /// Get the current thread id + pub fn sys_gettid(&mut self) -> SysResult { + info!("gettid"); + // use pid as tid for now + Ok(thread::current().id()) + } + + /// Get the parent process id + pub fn sys_getppid(&mut self) -> SysResult { + if let Some(parent) = self.process().parent.as_ref() { + Ok(parent.lock().pid.get()) + } else { + Ok(0) + } + } + + /// Exit the current thread + pub fn sys_exit(&mut self, exit_code: usize) -> ! { + let tid = thread::current().id(); + info!("exit: {}, code: {}", tid, exit_code); + let mut proc = self.process(); + proc.threads.retain(|&id| id != tid); + + // for last thread, + // notify parent and fill exit code + // avoid deadlock + let exit = proc.threads.len() == 0; + let proc_parent = proc.parent.clone(); + let pid = proc.pid.get(); + drop(proc); + if exit { + if let Some(parent) = proc_parent { + let mut parent = parent.lock(); + parent.child_exit_code.insert(pid, exit_code); + parent.child_exit.notify_one(); + } + } + + // perform futex wake 1 + // ref: http://man7.org/linux/man-pages/man2/set_tid_address.2.html + // FIXME: do it in all possible ways a thread can exit + // it has memory access so we can't move it to Thread::drop? + let mut proc = self.process(); + let clear_child_tid = self.thread.clear_child_tid as *mut u32; + if !clear_child_tid.is_null() { + info!("exit: futex {:#?} wake 1", clear_child_tid); + if let Ok(clear_child_tid_ref) = unsafe { self.vm().check_write_ptr(clear_child_tid) } { + *clear_child_tid_ref = 0; + let queue = proc.get_futex(clear_child_tid as usize); + queue.notify_one(); + } + } + drop(proc); + + processor().manager().exit(tid, exit_code as usize); + processor().yield_now(); + unreachable!(); + } + + /// Exit the current thread group (i.e. process) + pub fn sys_exit_group(&mut self, exit_code: usize) -> ! { + let proc = self.process(); + info!("exit_group: {}, code: {}", proc.pid, exit_code); + + // quit all threads + for tid in proc.threads.iter() { + processor().manager().exit(*tid, exit_code); + } + + // notify parent and fill exit code + // avoid deadlock + let proc_parent = proc.parent.clone(); + let pid = proc.pid.get(); + drop(proc); if let Some(parent) = proc_parent { let mut parent = parent.lock(); parent.child_exit_code.insert(pid, exit_code); parent.child_exit.notify_one(); } + + processor().yield_now(); + unreachable!(); } - // perform futex wake 1 - // ref: http://man7.org/linux/man-pages/man2/set_tid_address.2.html - // FIXME: do it in all possible ways a thread can exit - // it has memory access so we can't move it to Thread::drop? - let clear_child_tid = current_thread().clear_child_tid; - if clear_child_tid != 0 { - unsafe { - (clear_child_tid as *mut u32).write(0); - } - let queue = process().get_futex(clear_child_tid); - queue.notify_one(); + pub fn sys_nanosleep(&mut self, req: *const TimeSpec) -> SysResult { + let time = unsafe { *self.vm().check_read_ptr(req)? }; + info!("nanosleep: time: {:#?}", time); + // TODO: handle spurious wakeup + thread::sleep(time.to_duration()); + Ok(0) } - processor().manager().exit(tid, exit_code as usize); - processor().yield_now(); - unreachable!(); -} - -/// Exit the current thread group (i.e. process) -pub fn sys_exit_group(exit_code: usize) -> ! { - let proc = process(); - info!("exit_group: {}, code: {}", proc.pid, exit_code); - - // quit all threads - for tid in proc.threads.iter() { - processor().manager().exit(*tid, exit_code); + pub fn sys_set_priority(&mut self, priority: usize) -> SysResult { + let pid = thread::current().id(); + processor().manager().set_priority(pid, priority as u8); + Ok(0) } - // notify parent and fill exit code - // avoid deadlock - let proc_parent = proc.parent.clone(); - let pid = proc.pid.get(); - drop(proc); - if let Some(parent) = proc_parent { - let mut parent = parent.lock(); - parent.child_exit_code.insert(pid, exit_code); - parent.child_exit.notify_one(); + pub fn sys_set_tid_address(&mut self, tidptr: *mut u32) -> SysResult { + info!("set_tid_address: {:?}", tidptr); + self.thread.clear_child_tid = tidptr as usize; + Ok(thread::current().id()) } - - processor().yield_now(); - unreachable!(); -} - -pub fn sys_nanosleep(req: *const TimeSpec) -> SysResult { - process().vm.check_read_ptr(req)?; - let time = unsafe { req.read() }; - info!("nanosleep: time: {:#?}", time); - // TODO: handle spurious wakeup - thread::sleep(time.to_duration()); - Ok(0) -} - -pub fn sys_set_priority(priority: usize) -> SysResult { - let pid = thread::current().id(); - processor().manager().set_priority(pid, priority as u8); - Ok(0) } bitflags! { diff --git a/kernel/src/syscall/time.rs b/kernel/src/syscall/time.rs index eb49d735..7ba177c9 100644 --- a/kernel/src/syscall/time.rs +++ b/kernel/src/syscall/time.rs @@ -5,6 +5,80 @@ use crate::consts::USEC_PER_TICK; use core::time::Duration; use lazy_static::lazy_static; +impl Syscall<'_> { + pub fn sys_gettimeofday(&mut self, tv: *mut TimeVal, tz: *const u8) -> SysResult { + info!("gettimeofday: tv: {:?}, tz: {:?}", tv, tz); + if tz as usize != 0 { + return Err(SysError::EINVAL); + } + + let tv = unsafe { self.vm().check_write_ptr(tv)? }; + + let timeval = TimeVal::get_epoch(); + *tv = timeval; + Ok(0) + } + + pub fn sys_clock_gettime(&mut self, clock: usize, ts: *mut TimeSpec) -> SysResult { + info!("clock_gettime: clock: {:?}, ts: {:?}", clock, ts); + + let ts = unsafe { self.vm().check_write_ptr(ts)? }; + + let timespec = TimeSpec::get_epoch(); + *ts = timespec; + Ok(0) + } + + pub fn sys_time(&mut self, time: *mut u64) -> SysResult { + let sec = get_epoch_usec() / USEC_PER_SEC; + if time as usize != 0 { + let time = unsafe { self.vm().check_write_ptr(time)? }; + *time = sec as u64; + } + Ok(sec as usize) + } + + pub fn sys_getrusage(&mut self, who: usize, rusage: *mut RUsage) -> SysResult { + info!("getrusage: who: {}, rusage: {:?}", who, rusage); + let rusage = unsafe { self.vm().check_write_ptr(rusage)? }; + + let tick_base = *TICK_BASE; + let tick = unsafe { crate::trap::TICK as u64 }; + + let usec = (tick - tick_base) * USEC_PER_TICK as u64; + let new_rusage = RUsage { + utime: TimeVal { + sec: (usec / USEC_PER_SEC) as usize, + usec: (usec % USEC_PER_SEC) as usize, + }, + stime: TimeVal { + sec: (usec / USEC_PER_SEC) as usize, + usec: (usec % USEC_PER_SEC) as usize, + }, + }; + *rusage = new_rusage; + Ok(0) + } + + pub fn sys_times(&mut self, buf: *mut Tms) -> SysResult { + info!("times: buf: {:?}", buf); + let buf = unsafe { self.vm().check_write_ptr(buf)? }; + + let tick_base = *TICK_BASE; + let tick = unsafe { crate::trap::TICK as u64 }; + + let new_buf = Tms { + tms_utime: 0, + tms_stime: 0, + tms_cutime: 0, + tms_cstime: 0, + }; + + *buf = new_buf; + Ok(tick as usize) + } +} + /// should be initialized together lazy_static! { pub static ref EPOCH_BASE: u64 = crate::arch::timer::read_epoch(); @@ -76,47 +150,6 @@ impl TimeSpec { } } -pub fn sys_gettimeofday(tv: *mut TimeVal, tz: *const u8) -> SysResult { - info!("gettimeofday: tv: {:?}, tz: {:?}", tv, tz); - if tz as usize != 0 { - return Err(SysError::EINVAL); - } - - let proc = process(); - proc.vm.check_write_ptr(tv)?; - - let timeval = TimeVal::get_epoch(); - unsafe { - *tv = timeval; - } - Ok(0) -} - -pub fn sys_clock_gettime(clock: usize, ts: *mut TimeSpec) -> SysResult { - info!("clock_gettime: clock: {:?}, ts: {:?}", clock, ts); - - let proc = process(); - proc.vm.check_write_ptr(ts)?; - - let timespec = TimeSpec::get_epoch(); - unsafe { - *ts = timespec; - } - Ok(0) -} - -pub fn sys_time(time: *mut u64) -> SysResult { - let sec = get_epoch_usec() / USEC_PER_SEC; - if time as usize != 0 { - let proc = process(); - proc.vm.check_write_ptr(time)?; - unsafe { - time.write(sec as u64); - } - } - Ok(sec as usize) -} - // ignore other fields for now #[repr(C)] pub struct RUsage { @@ -124,27 +157,13 @@ pub struct RUsage { stime: TimeVal, } -pub fn sys_getrusage(who: usize, rusage: *mut RUsage) -> SysResult { - info!("getrusage: who: {}, rusage: {:?}", who, rusage); - let proc = process(); - proc.vm.check_write_ptr(rusage)?; - - let tick_base = *TICK_BASE; - let tick = unsafe { crate::trap::TICK as u64 }; - - let usec = (tick - tick_base) * USEC_PER_TICK as u64; - let new_rusage = RUsage { - utime: TimeVal { - sec: (usec / USEC_PER_SEC) as usize, - usec: (usec % USEC_PER_SEC) as usize, - }, - stime: TimeVal { - sec: (usec / USEC_PER_SEC) as usize, - usec: (usec % USEC_PER_SEC) as usize, - }, - }; - unsafe { *rusage = new_rusage }; - Ok(0) +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Tms { + tms_utime: u64, /* user time */ + tms_stime: u64, /* system time */ + tms_cutime: u64, /* user time of children */ + tms_cstime: u64, /* system time of children */ } diff --git a/kernel/src/util/color.rs b/kernel/src/util/color.rs index 385c56b6..18f7b702 100644 --- a/kernel/src/util/color.rs +++ b/kernel/src/util/color.rs @@ -1,3 +1,4 @@ +#[allow(dead_code)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum ConsoleColor { Black, diff --git a/kernel/src/util/escape_parser.rs b/kernel/src/util/escape_parser.rs index 0c42a975..472f4374 100644 --- a/kernel/src/util/escape_parser.rs +++ b/kernel/src/util/escape_parser.rs @@ -1,6 +1,8 @@ //! ANSI escape sequences parser //! (ref: https://en.wikipedia.org/wiki/ANSI_escape_code) +#![allow(dead_code)] + use super::color::ConsoleColor; use heapless::consts::U8; use heapless::Vec; diff --git a/tests/.gitignore b/tests/.gitignore index faa3a15c..a87be061 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1 +1,2 @@ stdout +stdout.new diff --git a/tests/hello_rust.out b/tests/hello_rust.out index beaee7a3..f821c520 100644 --- a/tests/hello_rust.out +++ b/tests/hello_rust.out @@ -1,3 +1,3 @@ -Hello Rust uCore! -I am process 0. -hello pass. +Hello Rust uCore! +I am process 0. +hello pass. diff --git a/tests/test.sh b/tests/test.sh index 1f1a8ca6..5f65c855 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -13,7 +13,9 @@ do wait $pid - diff -I 'bbl loader' -I 'Hello RISCV! in hart' -u ${f%.cmd}.out stdout || { echo 'testing failed for' $f; exit 1; } + awk 'NR > 25 { print }' < stdout > stdout.new + + diff -u ${f%.cmd}.out stdout.new || { echo 'testing failed for' $f; exit 1; } echo testing $f pass done diff --git a/tools/addr2line.py b/tools/addr2line.py old mode 100644 new mode 100755 diff --git a/tools/k210/kflash.py b/tools/k210/kflash.py new file mode 100755 index 00000000..518c5742 --- /dev/null +++ b/tools/k210/kflash.py @@ -0,0 +1,829 @@ +#!/usr/bin/env python3 +import sys +import time +import zlib +import copy +import struct +from enum import Enum +import binascii +import hashlib +import argparse +import math +import zipfile, tempfile +import json +import re +import os + +BASH_TIPS = dict(NORMAL='\033[0m',BOLD='\033[1m',DIM='\033[2m',UNDERLINE='\033[4m', + DEFAULT='\033[39', RED='\033[31m', YELLOW='\033[33m', GREEN='\033[32m', + BG_DEFAULT='\033[49m', BG_WHITE='\033[107m') + +ERROR_MSG = BASH_TIPS['RED']+BASH_TIPS['BOLD']+'[ERROR]'+BASH_TIPS['NORMAL'] +WARN_MSG = BASH_TIPS['YELLOW']+BASH_TIPS['BOLD']+'[WARN]'+BASH_TIPS['NORMAL'] +INFO_MSG = BASH_TIPS['GREEN']+BASH_TIPS['BOLD']+'[INFO]'+BASH_TIPS['NORMAL'] + +VID_LIST_FOR_AUTO_LOOKUP = "(1A86)|(0403)|(067B)|(10C4)" +# WCH FTDI PL CL +timeout = 0.5 + +MAX_RETRY_TIMES = 10 + +class TimeoutError(Exception): pass + +try: + import serial + import serial.tools.list_ports +except ImportError: + print(ERROR_MSG,'PySerial must be installed, run '+BASH_TIPS['GREEN']+'`pip3 install pyserial`',BASH_TIPS['DEFAULT']) + sys.exit(1) + +# AES is from: https://github.com/ricmoo/pyaes, Copyright by Richard Moore +class AES: + '''Encapsulates the AES block cipher. + You generally should not need this. Use the AESModeOfOperation classes + below instead.''' + @staticmethod + def _compact_word(word): + return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] + + # Number of rounds by keysize + number_of_rounds = {16: 10, 24: 12, 32: 14} + + # Round constant words + rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] + + # S-box and Inverse S-box (S is for Substitution) + S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] + Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] + + # Transformations for encryption + T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] + T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] + T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] + T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] + + # Transformations for decryption + T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] + T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] + T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] + T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] + + # Transformations for decryption key expansion + U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] + U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] + U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] + U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] + + def __init__(self, key): + + if len(key) not in (16, 24, 32): + raise ValueError('Invalid key size') + + rounds = self.number_of_rounds[len(key)] + + # Encryption round keys + self._Ke = [[0] * 4 for i in range(rounds + 1)] + + # Decryption round keys + self._Kd = [[0] * 4 for i in range(rounds + 1)] + + round_key_count = (rounds + 1) * 4 + KC = len(key) // 4 + + # Convert the key into ints + tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in range(0, len(key), 4) ] + + # Copy values into round key arrays + for i in range(0, KC): + self._Ke[i // 4][i % 4] = tk[i] + self._Kd[rounds - (i // 4)][i % 4] = tk[i] + + # Key expansion (fips-197 section 5.2) + rconpointer = 0 + t = KC + while t < round_key_count: + + tt = tk[KC - 1] + tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ + (self.S[(tt >> 8) & 0xFF] << 16) ^ + (self.S[ tt & 0xFF] << 8) ^ + self.S[(tt >> 24) & 0xFF] ^ + (self.rcon[rconpointer] << 24)) + rconpointer += 1 + + if KC != 8: + for i in range(1, KC): + tk[i] ^= tk[i - 1] + + # Key expansion for 256-bit keys is "slightly different" (fips-197) + else: + for i in range(1, KC // 2): + tk[i] ^= tk[i - 1] + tt = tk[KC // 2 - 1] + + tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ + (self.S[(tt >> 8) & 0xFF] << 8) ^ + (self.S[(tt >> 16) & 0xFF] << 16) ^ + (self.S[(tt >> 24) & 0xFF] << 24)) + + for i in range(KC // 2 + 1, KC): + tk[i] ^= tk[i - 1] + + # Copy values into round key arrays + j = 0 + while j < KC and t < round_key_count: + self._Ke[t // 4][t % 4] = tk[j] + self._Kd[rounds - (t // 4)][t % 4] = tk[j] + j += 1 + t += 1 + + # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) + for r in range(1, rounds): + for j in range(0, 4): + tt = self._Kd[r][j] + self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ + self.U2[(tt >> 16) & 0xFF] ^ + self.U3[(tt >> 8) & 0xFF] ^ + self.U4[ tt & 0xFF]) + + def encrypt(self, plaintext): + 'Encrypt a block of plain text using the AES block cipher.' + + if len(plaintext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Ke) - 1 + (s1, s2, s3) = [1, 2, 3] + a = [0, 0, 0, 0] + + # Convert plaintext to (ints ^ key) + t = [(AES._compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in range(0, 4)] + + # Apply round transforms + for r in range(1, rounds): + for i in range(0, 4): + a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ + self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T4[ t[(i + s3) % 4] & 0xFF] ^ + self._Ke[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in range(0, 4): + tt = self._Ke[rounds][i] + result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + def decrypt(self, ciphertext): + 'Decrypt a block of cipher text using the AES block cipher.' + + if len(ciphertext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(AES._compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in range(0, 4)] + + # Apply round transforms + for r in range(1, rounds): + for i in range(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in range(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + +class AES_128_CBC: + + def __init__(self, key, iv = None): + self._aes = AES(key) + if iv is None: + self._last_cipherblock = [ 0 ] * 16 + elif len(iv) != 16: + raise ValueError('initialization vector must be 16 bytes') + else: + self._last_cipherblock = iv + + + def encrypt(self, plaintext): + if len(plaintext) != 16: + raise ValueError('plaintext block must be 16 bytes') + + precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] + self._last_cipherblock = self._aes.encrypt(precipherblock) + + return b''.join(map(lambda x: x.to_bytes(1, 'little'), self._last_cipherblock)) + + def decrypt(self, ciphertext): + if len(ciphertext) != 16: + raise ValueError('ciphertext block must be 16 bytes') + + cipherblock = ciphertext + plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] + self._last_cipherblock = cipherblock + + return b''.join(map(lambda x: x.to_bytes(1, 'little'), plaintext)) + + +ISP_PROG = '789cedbc0d5854d7d53fbacff7805fe85106039189a390d8d4a283420231632aa226cd4b93a8499b54c801d104150543d2c6b7e0308ca84930471d0cf4959808896d53d351c7d6a4682b62faf1d67c28499a467480d168024460d40073d7dae70c1f2726b7efff7f9fe7defb7f8acfcf357b9dfdb1f6da7bafbdf63efbec35a498b4ffe1d79f16642424146458007644a29b2544ddc88e2aa87820412584c80c840132f20132071420f34001b20014208b4001b20414209b8002e430a000391c28401e0114208f040a90470105c8a38102e431400157264724282758c8dff28335194773dddb805fc25c30bf88b2ace3e5756c6fc1547ba72a5888927f90c89113181bfb4790770291c78e656ccc5cb2c61ec1c90cd36c4d1c4bacc9b7106bea0c624d98cb5a137fc85a93b3586bea5ad69a50c25b13b7f1d6e497796bea9bbc35e198684d7c57b4269f13ada99da235810983f461903e0cd28741fa11907e04a41f01e94740fa51907e14a41f05e94741fa31907e0ca41f03e9c7c4273011f1896323e2936f89884f9d11119f30775c7ce20fc7c527678d8b4f5d3b2e3ea1647c7ce2b6f1f1c92f8f8f4f7d737c7cc2b1c8f8c47723e393cf45c6a7764642fa89907e22a49f08e92742fa68481f0de9a3217d34a4bf19d2df0ce96f86f43743fa58481f0be963217d6cc1544be295a91189b2c840fb3011ca1281b4db33c6aec1f6cfa8985a103121d1915042b8190ce39851c2703319d631b384e56c0ce7b095705c22c33b124b786e162338669508dc6c4674cc2e11b92446722495485c326372249798b83b9830c71d2561dc9d4cb8e3ce92702e8519e1482919c1a532231da92523b9bb98518ebb4a46717398d18e3925a3b9bb99318ebb4bc648a01f57424984348319eb9a5132569ac98c73cd2c1927d918d9652b91a54466bc2bb164bc348b99e09a5532419acd44ba6697444a498cd99554629692992857724994740733d17547c944e94ee626d79d253749294cb42ba5245a4a65625ca92531d25dcccdaebb4a6e96e630935c734a26497733b1aebb4b62a17f580a8825016810fbc995387ba7db057dcd953ecfe1954892180c72752ec656d4489222b19f47feb72db791587fe527c02772ae99585fc5df8ddaef5af85deb2249d2b5a02c0582b6a7dae07923f01a89ada84d7b5e97ced0b8fb42bfe3f5df122317f983d6d7f4dfc887dfb21441948a3656162248bba5e2a12bb9f3120a4846c495787ba21a7011df66a9bf9d9cda80ed88e311eac2e4161362de4408b7e0e41db2944f52cc12a3d5abf14e37f01d274fde299b1208d631252a8ab1e54a4cb424116b8d9f44bb7a82d63d5ab9583759ea1853e5ec0eaadddd6394ca569ef2412ec7de74b6ca29407c81285d87808a600b4492e26cb02b79adac3a4162d5d3027ba4accd6e130f115fa5d86b7d0dca7855a272a28cdc02017e4f807225694dc7d96bdc5e49549d13c82b2feced5fd3b1fb2bad0e9bbe1baa836a8a207afb4cd79e2d984eeb50691a5607ebab666698dcdb5b45c75e8905fbc41e11bc76f946323fd1ca0f9519eb8972d3361008abbc28f2aa68c77667e5a2a5c417d3d4ffedf5597615ebc2357407ad357522d60f65c032534c07ed4ae187245a3269e56f429d5d20a1f2b15c9401d268799cc43c30cfdd7dd0befdaa9409e5946836176d2ff40d6b2ad839be93b4779cbd0c7d81d1fb028b72693a93a25157d1d2f0f675c409206f93e8884ba394b66d2db46d9948f5a3149e8134a0a73201f474c8ae6443db46829c674436456cb37b9ca7892fea066d8b6d5916096d598b6dd90bb2f45953b7c1fcd0115479d4d32906ec27d8e3b1ac757c273167e3bc72bcb33d2183515ee42364219fb427146f90850ea011da3c2354e0ef3055380abf8b713c3c0e756728df722a4b15ecc0af81df11f03b037eef87df1999aa80fa3a8af197a9902f948df17fe248e289197463bd336082798ba1bff980a84ac51a9f457ec6005fd3a34b443dca9285b6018e5d6badc486eaef96a8aea3689a990116db48e335fe4c6b2b8b96777200da2af63d682b16ec2c0b7636d4661cea8fd623a17815cc814c7b02c9d7ea0f754820cf38924a3599e6047a5126eb66e9ba2ad568f99a02bdd03f18e83b5d5a5f19ec1b56e14df20aff32f6917f0cef3f09c05bf68eb1df607a6b32c4934097d86622da189c4b2206fb1cc13eb7ec778e74cdfe584917c17ca04ecc0df2e1be3d9fb307feeff291854e1a1fcaece1d23a691c79fd84a132f7a9d036d6919da46aa9c0bc42a993d07e46fbdb9be0071c23d6519de49551a88bd8bfc0dc461cb3196c4f46867e8efd08f23f2cf31128d3d5684120be91cefe505b0d6b27bd6e982fd68de64de5dbdd80365bb753e7b06db8f4749f2ae693233b4526c5dc4a6cb94d217b764e8fd7ac8a166a97310e3e0fd934592abe05fada68ebab75d8f738e089d61a970873050bf332b6259be26cb3635d70bc2a850d4085c1719bd92a0e1db738fe71ec0ed8b7a86fb16f540fcb2e0cd183d62e43fa0a8e75aea147b775b1d750ae23622bd85a830dd97448b375ba2c2803ca0369aed23c4ef6186c1d61b1efd2b6e4f5b6c4fe02fde01581b6df6f86f7656cbbddd537eacb909738682777ffd2684321af5d374a37d8e776bfef48ef2472209e281120bf3f1e74778ebc22d131f03707f4472eed1cc63ba98d79d7a7981f1d97a981a3ba1d48d1c7ff5143395f69fee80c0efc4cce9afa4306fc4cc69ab8163016fcd36de0b7dec2a961307e360378117dea08f0a959f716c877cb9623ca4d3c0be5f1badfc6815fc6815fc6825fc6825fc6805fc6805f16f21304da6e816b638eb8aec37cd405e3932755a5309ff25d76b99c8776ab421f9e757847807ec289ec0c23d16218f1174b443587b16a13b471e994b947c2ee606ce1d7896f27df0b762700795fb726cfe5e49744f4b7a13e591c8c1146fed1678cf951f4ffffba30e93b847817a11ea0ce09b708e05f439da1aea963a94f4eed4439f4df30a8e71691d8c23733d123a227576dde7c8b521d86f514a09e02f54fef80fade01f5bd13ea7b27d43705ea9b02f54d1de8a322ad2bf4a5aad23098fb931858bfb068d794e622c1111f4e3c2e17f84d502f298168fd8dd7fb5ba9ded7a0af94f1d057c28836cf94403bfffda2eae6a91e52f8edc416768d1c29df857db94795f6df301f9cff302fabd83924bf0c0269be82349c754a6768beae711c928866371a77a34d3be2f613731984e7cfafd67ca246e2f1877865d538a6433c6b5c09a972a1ede881396ac91819ca5285305dee3141185f5d50de082cafca0cb2c585ca75550e969b5e39bcdc13dbbf5eaeb86368b9a13261de996cddd7331acab90ae58883f572bdc81dd2e64f0c3b1aa517dd4ea00dc20bd4369a0360fb0e415e601b217fc709f1851473d3f03a59a04ed3968c81bef625e43dfaeb7548df3abc8cc62d5a190d9b6e5cc6894da13206f35f8ef95fc6390adae67d55c436dafd1eea4596d2373a664b74aef1d2b1bd0dd798304e67301ea97b60aee17ee7228e3a91417f470d83be156802ffbe878e3f47dd7ca6aac90c3c33515e944c49a568838e331ee701d24eec2baac0762b7f95448fab146d3253259a896c061f7629f81abf6a2236e922cc9175e0abb9982433ca2431ca534de8d78d86b981e5bc615a39f1e037839db2de1a20437d0d18330c8c9981798cdb3b9fc8ebbba1cebb7b64a939e8abf0f7d2b20fc17a269dd619faeb3c6273b51065d219623d0ceba0037e82f5f7b8ba604d2411f457ad87a19d0e34a1aefa75ff6314b5b93847829f87e3ba9d1cbd1ffd20da1789fd0159e091575825a05f88bfc90fdb49f17dc6f9dfbbb43612da273fe5741deb2ea2b674250773a517d6698e7817e309c07c297407e50902272f16385b6b1db1de5e47fd514723f8d18dd00ed021b476f67fa9ee943898833819edc0134db79a97623d5d8cea8e833a811f5ca7b531f51b06da18da16db199eab6083e55290196c32da0eae610794e3a4beb53cc1496c39dd10de0b654a0ccae2683c88e5331e7f1ded67ca5301b437fdb01e1f297707c6803df91b9d83e2c126609b59aea1cdc2e7e18e78901de61ddf26a917e61ab5bde33fdae97c1c23807f7db66fc8f3eb8ec6c5d0a7d3a0be0d7a7d9b209c43e4322751c1aff0f87b20bc1e7e97319e36f8dd789aa0bf1fd20dd7d88a7d8df1e406e07737f13a0f7fae3a8599b219d6c2c08b965cd48e295130073606824a710ff8cc96fe578abbbebd8fa589c4d358477df7e80fb5be7f44eab07b02ddc087f51bb41deac5374ee887b536941308d27553fa76c22d2cd5e58b67b885af12796218c8770df807e07739635b798d448781decc0b39a554d264005986ca615e8c3eb4d08af2c0faec4c527730685bff3ad8c317aea96138ff8ff958f37166051de91f806d3b0e639267b472611d3fbf05d2c3b88a041de6f640b80b7e9731b6e53d302f8ae82b3ac056b472a04f07e81ef445505f0ed027ed03a05b07e853eb039aad91253b51cafc682fa11d632f737b0506fc877761fc34a3ce921cc120c87b0dfa9d08fd10fc17dcaf82f9327186a0d7e7231b9385e3e76e6f39f65f86492a0d06555718a7f5d1d23e4fa99b24e133b19ca5eb08278eb1e269aa0863cb15c6d8a426184bf374fd384f5b130298df9dda1873d9d01f45ff08fc5956dba768fada3e45bbe5681c3e6fb7905b70ae71e3dea0d3f96b8d7734de0b360aea5b7ca52e3de14a2e9fa09595f62e8cff44943924afc7153fd79a08732384659eef439e1c66e9b395baa8ad0179188c4fc7e74118431582a8f3d9d098f56d957a43bc81b8c82f967ab9a412a2d91e9ebc026b256abf4df17d43e382ff89766914fab10eb019980675268b3c5d5fbd62c231187bc4ebd2f48d32515debf95af7d3f461d41712c8c83509c5df0ded31c91219a94c6ac5b5e92858cbdd0e6bba5174ef8661983509e43bb2b47f942faab5bf3dc13e0dd6a920c3b27e7587c0525b7a5a60b5b5c636ea8fa22df5f0078912b37d60ad04fd7dc0a7e4d240667e3cfaa657d146d0b50adae2c497619df2265d43396631849b853e018c33f47d212d5d3b829c8e3a9883c6b5b24a25fa45b04eabe9266b889db5d680ad813942764ad4b6d23daacd507f58bb281b9b58b40d2877955960b03e554dd25cdf4dadbde84b40dee0e71c1da544c13c59d38af66d24eef5a8e2a95198ce1779fabac6df8ffabf7ed7f7a7b994b57f62d5a20039e67bfb9cfa9418bc6bf3ef778ef48df4cf957cab9afac79edf58ec11ff64df2ce2fef5ce9db9c5b5c42a79c92ba217bc2ca00250c02b3c50c02b1c50c02b2c50c02b0c50c02b0428606331ee19a93b9de4f473ea4e81a1ffb3a79f7bc4abee8c275a19651bfdc571d777161f33dffcc29c9dbeb57fbafa0befcd6bf75c98563add39bda5ec723b6996dcb9d803eb3affd0ec4cff434b4cd49e9c3997cbd3e79c4a5d30ad5710a65f9dd63572c536ff6488df4e2cf29e1c871bb4e58ee7941f48a47c3957d7107efa056b6537b956d467bef682b5b63b3cc134d909394fee5b3af6dcebe96d99f3b3e738a7666d2cd6c769cf3ee726654d477d609fcb2971609757056dce3afb86091b8b99348835014764dae435a4e6c1b96327378b2dd34aad510789756203f40658b92602e284706b2a500bf4b429027bcbfc923418a5e1ed1df57eb7997a567d31bdda2f89a4366bbf5c7c6a8bf62b572257532fecc964b3e7325aa9ccd8b816585f8da82aed0e7a4a8fb38b9aa7974fbe205e569da523a12e3f9c56cafc8d963e652a9325982e80ed18519206e5354f769ecfbc277b4f36bb5218a863a31f6bb8fb4aeac2972fcd1d2bb64c6ed6d654755fa67efe5cba754af703aae47c10ea95dcfd00e4fa80298799bf0d72bbbbd3b44298bf177eedee30e585cd7f077e05db9de9a92743b2a2a4ba1ecfc5b5942a8b9ac5cb6f2d987c615a79893fce09a3fd9a5aca3f50bca27a5e5f92724622d599e57f29cd6ca8e7cc3c33aa112cd58856efb6b44518b37353f386ce37d371f666e60dafc5b12267bc358e0fb726809548e449cc496b2afcb2f0a06d5ed7f63cd0f6d953c0e7810ff61e60e14d39f52823fed3a4ac6bdaa4683676ef791893091a37fdcc00f7c21ad23c3de689127f49fa1ce7c6624793c04c5cbbe7ea9f3bdebb50d65bdbf5df97cfb47cdcfc93dec7af2eef7aa263f5e597b7ccdc3cbd745af9bb7faf5e75ecef0e58b538ee08d756ba51d268e796924530ab46326925f732f72aa61153265efc51d9eb693f81fe5811ae48c214413812be7eee92ad4ac18f991f6d3565c6e48dcf50faeb26cf9b77b05af9b8d4faa933aeac9d644c88c9dce35ce4ac75c734b747d8272ae38488239b5fb20b698a3b9ce093f68866f38656475238535b651b516587b9cb6e4d7e89bcdbb5ab863e27a7b2fb964e7df7bbaf397e1f461c07cac9b1b0be0f6bcb94822af271596ae6c8a74be296942d726e742b0fd584633efb5e2adf8ae3c09abc8f3c73ad24bd2d7f7e616da1f84cdbcaf94fd43e213e79fe67f73cbbe75976c3f955739c61d04649234871c9a29279f2bd238e3ee6c552b8b7b19479f36a5dbeb051fd1ce8c694a93e2559944de26865e52f4cf07bc13b27f88c917947f86a76d47355a3c03eba6e269fb98f1cdfce29dd5d44987a84df0bf3a6645ff8f75a5a0bfbe409ad5a1f415dab7e89b7467591bea5cf79b17cf5de4b41e607f2bd5f0547dd577edf23de9179abcba6819dc85c8fa5efc9958b025e39573259a35a48cc65fed417ee23a507e6c634cf3bfe99ebb352a5a08f7ce63265ce293be39c03a99a373c3c6f4feecdbb1f3e1e95f97bd03cd5ee4bb651bf18d0ee8e8ed683716554ae9fef733dd63d328fb6c4582a5b201040d9caeb63f214f062a15d8bfec92a05bf209f95c64109907fd1a3f3525c6e7b4ce6c3c7b165d3ae682d95f1b3473e1ae5fdd8a5f87ec97ce63ad2d8c42643fb60eb1c296b6259f7be52dfbd353d8cd77104747ca49c3069c7c2167baac2a349ce11e5de4f08b6e64f685b42cc1fd47cf2d1db8f977d8ae5e53f3aef7ce911976beea3c76332cf4389eb8f3cb6764f735cef031d4b2e97b52cba7a7f17acb91e60e6098bfa7eb4ebc264676da6987dbef09e67f63cc3fef4fc1359d0cadcefa087bf5e4afaee503e0823a59917eb1d4bc318e5d9ebc461be8d51362d65168165deb8634fcec2adcc14392c6cc447f58365fce8c24f5ab472a66d7e63b35ed6bd7d3f2ebf6c2ceb9e27f73c79a3f23ed2cafbd9575a79ae22e6cfce079c1b5f0a95760e4a8beb5d74b5ac654ff3fd5da1321f6f564bd73330f7b2d3ca957e77063866c5c72aa90fdee226ca970758ddeea47d5d0a360fe500ed65aa527eac72d1cdab4d89a4243eda6526d16633712fc75dbcda31a3a5d96e92ab8a525089fc809035e5f18ae41263b2a3f2c6afe8acdae3aefc62fca5ca5c7c8755e9c756268f82cfc3ccad979bb6e3aa8063feb22d5df1fb4d6a5336787c2e2e94ff68492ba12c8ce62f48c10d694ad8011295bdb6ca26baed4a4643785f9233debd126295df267107c2582dc572e9905309ff607454f686343deee206934782f25ac613bec59d033edc816e91d2b7baf9ededdb2fa9cb23c976ff647784efcdfa77d39556f0b625afb0a35e95ec024847d4a6e3b806149ff3026782c6e9024e915452af8af6c5a063c9e76beb529ba63055ae46e9bbf5e3f39405228959b1bdbdfa338fd442badf9e5d5ae2356399b34ff7533ae7742fc699ec8ec955fd6612e36f38a88a1db1cab9b6c8510e263e7a49bcdd39dfb3f30411e2b943e5ba56de7dfe7eb719ea69b59db93a5e9141ef1197c75f0067f58be8a678bbf07d9bec231cb8f6cae3b9a2e7a95cb2abadb34ae4f0d7b6f8d64f37dc3fa8dfb9e6fbdd776e9ae3e41237314a143f5198aa3e358bcc35b55e577b9288522dc6603e6afa95e0b517ccb9f4bd478703e41039a5653951267231dc9130f022b710f9a7c7834aee4bc423bd64df90aedc1bb855f3661a9f2e56b45ff1f9db950d4be3b6cfd9aaec6e8b82f98fd5c67c7351dfd27d9b15a1dbe291ee6594c26ea2c4a6c4ba9fc41dca279fe2ea46905b7663ab5a7fd556a8542e35ab3bd33965bb18e9f086310f6c97b3c793623fca06ebf0b50e881da5e02a994a6b0be46b4f72d700b7307013e5260656afdd272f71936df395f3277875492eac65cbb8e832583f464692d1e2fd3b65410c463cae4c6c1085f4aa3371764f6413d5e847cfd31c929a9a4d97232e908be48b670ebe3b5f693d41aac443c206ec07d0574e10754923e4d926aef7026782c60900e7296965bd2c405f11a1afac68b8aa2e8967aaca4e483ff4463ca1880d64fcf2ce977c4f36f59b56a779c76747e475bee4d9b4d3aefc781217b562b0f746b4477da6d7ef634a53021fcafe28b2b57deba54aff9e9dd6fd914cd6ef94bf98c89e9dd5cbb15f55b7c5aca8688ff96ce19b334b6f791b476a94d297e41bddd43ceaed6df122670b2c2739675b3dfb5cffac77e6f64973b6f7156dd8ea3cc8cd1e49fa962891674cd886d6faa50faaddd74979c3b1748fd36b7fe32567ee1f762cd9de67deb0d556eab4ebedf9a3bea5caf86eb2c4c9ccc3f6b0098be7bebc7bda762ae9b4c0c3d86234b7c34bff663db099f8d89456e83179e99c33dd7a00ac29b46b65eeda2a8f04a336c74de0d9bc008f391747bdfe078febbafd77bb695e53020f3d57ef4882bec76fee9f5be9dc72ec056be25764ef7c8fb40546f2f64bbef5ae4f5561044367965b3b8f807d6064ff78821a896ad9a34934cb7dbcbae5d78770a7d777c9fb0658a771b8d33bf10fe8d34fdba98c6f20e8953ad26195ff9cb75f10fabc9ec6bde4175b6c520329f1ff225d696f24fb5ccaca565807384e08c4293985575e68eaf77de6ef879541d17af2f6a57d2ef52957502e137b95de46d1e11188e3b74ee2a81718aedec938fe20b0dc1f9c2ce71138c7612707ab601e7d64eeb033687dabe8ba75ffd25eeb81409ff5b0bfcffa56539f757f63d07a20b7df7ad80ccfa5e07aefd072d59e22d27a7d6e3db66545d6cbd2cb82f5adc67edf2590c8efefd9588c722b6d7b5166d5cfc24ac8d5406e7edef74c77bfa7ac916c83b5519fd97142221bcc56d244babd4ec171a23be89430ef6b5ef42771dd5276797a696dcb9f9befef7de0ea92ae1f75fce4f26f9d6197e767b765d6668b2bcf3f73cf4ff7fc94fdd9f927a766c1fa62e7becd335bda3b7ede0b6b96d9a5419c8dd472989546f041f9da89a0723d0937e58ae5afc2827466eacae56056aab94e9845b86679c069cc736331bec5f0ad3ed2ab3cb4d2a68df1f8ba88273daeef40df514c3d445eef0fced961ca11ca175e84be14769128937a585918c12a7f7addf47195a77ce55cda0b0f16ed552f4e84d92005fcd7d2e012e7e4cdeab589c4737125634d4d0ce24ae060bdf2ce4aa1724df59395ed319738ef16e2295b3977c37c618bc795c228ebcf4cf3acbc4473f0389f64d43698475a2b738f094b9cda9cd74ef26134c04ae2b2bc7904739797492f49776e56dc628c239187354e959dfb7d2958d03f0717bda43c2f8df384a58335ee09572590f6c4b571952baaf364b13c286fe69998f6ca4bb254cc2b4aa34915f3d9f75c4adf091293597d2126bbfa32ccd39c72ae919f0bfd8767a3322b7d51d995fea81595973cae6c06ebacdb8bed5a9d9398ea6ce716cfc55cc67a20914119979d7fe660cc8aea8e98bcea2e456a2368a7212f6e717d4c3efce6aaaf7249d0822e9eafec4df3ee010f04d62ffff88157f9e3ca30d41095d3c933a829941be55551de2f1a7959ca67950d8d04e4ba00725d9651da558d642e0f29d898cc987331d931ad312b622ed66e59f93648dd1195570952f45029200ed7895238795e93c4019254f79e032960fdf35ea757b9ff09a93aaf72454886ffb5b2df2b5fe8fd9f951dfb974e2feaee7b17a66df68d163ec367b22404af557dea562afdc4718733a85e7b27f8e75def7a50362a534ba3a8cbc44765469d8bca8e6aa5326535b2b2043eb42430e395edbef1b9dbfd514f565edae35c82b6f5c76f825ea22e46e5457d4e6593a0752481fd086593782ea6737c2184b9f1ddef7af7b926bb36d453f98ebde3fd73f9cb1ecfc58bb497867cb1831e5cd5e30e04ae05adaf495bc596f6840416ecf7c46ea2af9fb7547f7fdb9f628e8656bd98627aa963c75406e2920e5e5928904757ec733f3ab086c715c086a20db803016b80cfbdd47ac25af299b5933be22e2fba707f8bd8a506a4887da5d34b67961f2adfd588b995cf67e683edcd1b9eda31610ac316eec9ffefb245657b56deb332ce793e3b2d0b56affc70398a57f42dc5758e12dd4dfab4dd0f481d92c899fb110d2fb8d0e7c5146b48cdd850cd7feb9c5d46edc0ebd2b3587bcbcff4da539fc5ffb3e1b58715bfc63f1fd7b2af749382fb12d34bc31a5217eef2b53707afe1da5e354bdc3155ed7191d7bf507b24a6fc8bbde9d54761edc5e3da6bc3626d758f6bfb6d5b7634de9eb7ed1d559a2729ce6e12072d7cf42e5cc7a316607d4fd480b70f258fc98335185df5b988162e6fc7b02c110be8f256dc0fdee3c4f43559941f3013a13126c31ac7d3fdf2980f62f298b48fae38ccf84c32cd3a1b5a3f3aebcbb730e9db7c51190f1fefac1fe80fa8911a290f35d27cff508d343e61d4887879f285b89645cde517998558b745ced0de05b6704837775fc0b2af9dd5a51453574c83961ff9741caebb1f14c88647b495a3642a6f1c9987712cdf35ea86dc8ebab9fdd96d0df4f97770a7a3bdf96ca7358a27edcd3fef5ce8d56494ded1f6b57ede833b64d8ef64b7c4cd55e32e8b5dd8ff602e26289b52214608b9b224157ff442651aee9c293f6826871f511635c39cbf279f2d3c9f7dcfca3d2bd3a02ec9d91b9ab8d7a145762671d61a9e8c7c5af9503465ac40a9b76dd9d51833efe6e753f376fd29f561a0d9239f2e998f35eb5bea5802bdf2e66e526d5f9fae3c1e302549768b20d9960688b065d65ffa8a5457e0e75179dd2f54b92506773da39ab155b0ed657712a73c2c11cd9baab66ff32db8707b9eb5660a73f0dda15aa9b909b55252cfcc7742992c9439adcc37a1bba7da7ef07d28d3e9278fa42bdbfd3ca6fcae57f58bb4dd372c16bc8edf3919dfd6b00eee770219b5c5ba7f16931276c7dc92f4b1beea1f3e7adcb7ccd28eebe93f97dd5f465b29bd6660e76143fdbe5d2961afdb3beb99b13806a6954e84762ef74f6f696fdefd8eb633b9ab71b2f39eecf399027a007eec194c1aed154ee673bd9dde08ed3d09bd6b3ad63ca7af137e4df724afdfb176f2d5b8ae451df75f9ede22f64e2b773f82abbc776e6def680fa08fb827a734d7992b9706fa3a2b476d8ddb3ea378c3f899bcf25817c13db5bf773369e7f3ef29dc53c83e737ee52d506e89b6d7f626a6ad75fb2675f77a84623bf2cefe06741cddcd6a31826f7010a30fbcc856bbef1f3dd7b515e5fc57b73faeadd0263c4757237b6b7f95558f79956c45afbcd4af499ffecbeaef2f72a694baec740ced71ed6bef18f3b990b6a35e291626a1b6e25aa695be512a5e6eef5873c97110e6d2323ea8bf97895603ad415b590f895ace250b8cb37c6d9532f124ff7b77f945cecb07773d5ff1c4efdd95aba3be88b824832c956df8fed19ad814a5af7f5e8255ce677e7da4c6bfbc55990cf5a9bfa4b503da9c351dffd112d5c62cd871ce71900fba1f86789b5e1bffdcf36fb8b73ea16c9e10e6385cc658df9ac0580f1f261ea18d6ccd7196ef68fdbd3bea09e56693e85e4cf709c72a66af685e42d79fe3284d391351f945e567c2024d2a9bab87c4f87c134c576509d6b2d9015e6e4a22fa6a36dde36e241139efbe4053ceec195925994995db4c9e97f6b92b15b94c0a56405e30f3b5bfec1516fcba3e6235ae771ef3ca67dc646c3acc8f905b2ebe51e4a245b3be629bb653d5566c950da2333d7a69bcdde6f693989cc75e70a386eafdfcd62f2a3edb7e71fc17a7dffe61ba92d308e90f09c7bc32aeed731b89dc841cbfb8013913340ebeb52c92d6d6abb05e93716d7fb1e1aadc14cf444b8dd223b85e2b6b80765a5be5bbe4ef1f09eb356d6e1c36a7fc468a440b4aa4a116b42e72b8059db536aef50d3a934dbe287e3eb354f90456074d60ff6b4482f67007f830c43eeab504d36cdcf71fbb67b96faed0ff65fa5db942ceb517c27277bda07a6185f7478c15d30cbda8f8b61af8cdc73433699f652ecade973d2a6b700616bb700e2edf32bdb4fcefaa5428c534c75c76a6c79c0a593bb4750f9104b08fe0aff6ecc999560e36a679f7c7cce7fadc6179e6abce2afaae19ad48bddb8f6f0e1b37bc75196d8309d6c5601f4c74f6cd8d39f562fab6bfe31c4e673613f30ec6c1d4669a1a43f816d61a358599ebfd9af66a241eb46749786ad8fcc37edd1fd152d134b512c134a7560e4de30ade380d4df16ba91753743c3134456eeff014f82e4bf9ac8e95274c25b052613dae3a7c97a99d8b2a321365b4a48723083d935de3d7de73091dc1d019c381f384fab9808229f6ce82dbec09e00d9aae78b5779ce0d9b336a985c55e0bf6f149b55422367c1739dbc5289b45c2cd92e879447fb148f0dcde95223ec11b4fdf936eba52272514442cc4fc588fd4c5e2190b5856314a84c43bbc2ea26c95d8a474c2cae313898d6f215c124fcf2c5813befe0e5e760a03efe0a12f51f906de07eaef12e9f9cc5281603dae14cdd3e4c7b3181dcb3ed7e57f4c1e3f8fe07b583c37ab94818e6695d2b36046b9f18cfac0d90d274f86be93c4736ef4ec6fe258be9d54dc238b84d7ceb63230ebffc7313c8b67b6e1b7265ca76c2211f2d2257635328ea813e2417686f788ef138fd38be76a88e335a1575edfc3549d71316a4f2439b2b39678da22c18f02bfaae674af6c8ec7ef1e789bab8d38e2a4deaa263743cf1cb8ebe89908ab25d01b2d4612df24f11ae44dbf5f4912be93218bc504cf6aab91f1c4239e86bc0e8d8630581033ce09181ee98b68c47390fcb0b3745207abb4d545e1d9737a3e22cf1b89ef6ed5d3ad8c1beaa43afee4f3383c903747b0ef459f7112f9a91e5235ae8c7027385275a681603d6ccb9710ef973012377e19acda7982d89e5a02bc33a4eaec69e26d037e3016e6b09e60f4d956e2790af8bbcf125b5b0fa33e154ba277b711ef098883cf9bbae9f3a4b26050167b829eb63606fb77b439403c3d2740576da4ea53814992e079cf2482df2c447f3a81898e14195ba08d789683e52c9a0456d90c71fae9370d554db3e833af78959681df43a04eabdc498cada8899e77887e3f8df1e239efa764d017a4f9703153b5733ee3290a101be85d0d4491aa9d4b214e73109f233ffac31c06dbc42b1e0dca45515afdccb9f44c0f3d2b717a3dc8504c6550bb27104f00783b90f79d0c8fdf4b6c421db467b7f6fd91fe5e5a7f0f1da4edd252173e709607c7ee90335bd684b9f47b013ccba3b71b0fe34ec2ef97948ddd3c9edf4a0aff4e861a46b86801cf984c251ee93a3d53a0060263944a188fe20ea20ac512f81f67a3c326125fcc1d7d2a8c1f7ad6a263f7a728d78dcec670773254dec1f1d931f05efd4abcbd537649bc572423657311c17e6cb5405c58cc79cada48128c393cbb619d2c818f29d1b3ac491219a90a11bcbc338ed8c446882f92aa32e8e39587fad59d8d34be57b440bf8674382789e41e8fab89752f41bfe8c454f88deff1793cc3603d2041da1e682389f1558afd984e953244ccc311974ecf0c29802a68435fa5d43ff48c47e83b00b0b5b03aaee3b12ed14b170f9c6153c1a878a40051ddf1588f9168bbd0e6e279bb941db5c4965347f02c8eb5a66ee09b8290fda57936a3fd1658b0df23e5c5ad8ccd89ed7ff06b71a90ecd022b0b161ee2d0b2757b66b609ad300ed3084d9b70107caf8681f4f87d8dec4ee79487a4d1d036bd6023f1ec05f49d6d60f76fe1aca93318ccfb0ad87cda3edb41b7a565a07f0be8b88538a6a413ee36d05f29f4819d3c2b2f1141de3868b75ae2a815e95925d49fd58267f68a4d0549e9090574ceb0f0a00b563b8bd6087a81f1e54ea7bac26d35251cecee41173d6fee27129d37bc58260feb8cc9dbbf3e87542c4cb05aa630d0f7f07b2e0efa1e0b7d8f85be179a1778aa2fa7a0b7573303ba1d8fba3527513ff463aa5fd0153d638663cfe5a7e7dd92a479198e78895303fea0a7d54bacb77aa98dd7f8e983fc69c3f9553b26d0b390784664903715d7c7c08bed1be4cd025e119e01fb6a909786630ebf29b93ec85b8cbc3ec8efda202f07795fe1b9f241de7ae40520bf9e419e1379ed905ff7206f07f2ce427e5d83bcbdc8ab87fcae7845e0d5cde79202c160d58e8368377f8ef688d675df21edaca21ea76a4703c178b27696ad6f907f9a2481b785e7240779ad24a91179cb86c4eb26492ee4c5f60ef2349b0df95d1fe44da0361af2bb36c89b0abc5eccefea206f16f0ae627e81415e1af0d0569eed1ee42d065e333d273cc8cb01de51ccefca204fb3cb90df97833c27f008e6d731c8db813c6cb7f641de5ee4c16a71d91743f482bc66c8eff3217a41de51c8ef12f2e077b1ae1b464e47bd2ebba0e2ee1bd899c17edb78e89bfaad962f8e55d24fe734906da0fd5e3d346003b478276e1caff6109e71d5db49646e18e7358cb34cefbb2e76603c4cf1e219ab6b21fed7c703f28ce30179c6f1803ce378409e713c340e961d8765c77e19e27fbdecc61b94dd7883b21b6f5036f206c68ede0f4fb0a1f6a363801433213eb663a80d87e815e5bb3098df405dba0679a1311b7b6590171a9f673b691f8131a795ad8d4f3c4b36288f362607c700f242e3313406a8dcfa780c8d01e485c663680c202f341e43630079a1f1181a03c80b8dc7d018405e68ec85c600f242632f340690171a7bda18d078a1b1b7fbb341dec098ba38c81b18f31706790363b46d903730465b077907917715f26b19a2abd018f50dea3f646fcf7e34c80bd9dbdd1f7a81c7ed4d27fa1a2048bffb4ac0b38cda3946f0912a71bd017314fa49daf77e7826b223d6ad4a15ac72a1ce143a43192d4d059f2e9f512ef8595ccb79dc7e68a778686777bf0dfa119e4b4e4aff90d0df31e06b0472895221f1da373ebbdf367e2b3b303e713f764a80c5713dc893f05bbafe216196ae9d74ff02cf2cd3efaca41a9837fd9afd89a7f6e7e9a1f667b8ed1159b57b3d8976c398ea2eeaf5b4b6e29a821d6e77427140afdd015f68ee401e3e8b76ef20de6ef0f39dddb19e6e2f19621338b507fce06e09c63bac5d6ec7f1be5beffb659cdadd0d79a6619e1db4dc5fe178db7d55cbbb9b609c68b73394f7182def909d0ba56f20de09f439f6a97ee4e1f368b7c0601cafd039d49ea03c63409e0e2acf742a4fa75ede18e84f5a79c2607954ae37a81d68c738a1bcbd0283639ad3ca0db5512357b5d309cf08837c5d5eddee419b0f2ddbe2e5414f1707cb16f91b965d7308e22df37fbd6c910f95adeb83c738a1e7a1f2691eaf621ea1f1e1e28d325279a650799af57a0c97350e9fc59e1da2a71bcb5a8be5ecfee4067ae20d7afa9a0c43f4240c2bfb562c7b59d3103d09372cfb352cfbec0737d09330bcec745edd297034fd19911ba8ff645aff53a138553b0546dd01fee46981c37e15d21daca139fcc603f9743dd88ab22fbb4cedc2f9bad983632e7dc1d03117b217686742e32f645fa86d21e43edce331f09261ad40e898e7b17c9ea5dfe86c94e89a06bf2d6827c50b908fe5ca8b0556fb8e2709d61466625bdc4dd72df45e05c837646364a1986def88ed463be8a883311a08e0596a51e3efbe12e25735a563ddfaf5f85f0ef2a91dead5e3770cf21ba98fa1c76fd7f812c89484f335ac03cc246571378ea58016e7ece5c138b944b365c85f766990efc63c7be99d0384c40f96e5c23ed385fa011d4cd5f8d0b66e68b32689c3b95d7b76d41a7a8672a30cb8270c6903faf35bf43cb19ff56a738324e0f747d1c20452659e40921ab632a0c7d770de0f3d8f5eeca4f242d9095afa46d4e3189cdbdb89fd7b219e2e673fc83e7d9087fa5b06698fdeaef370dc60f83b8361ccfbe8b45018f26142e56b7ad1f63a944df8dd8144edf51a7294ca2a83aca1b2f07b7790270efb01f6a3c1f44dff427a09e5f8124f887e3dbdff5f4c1fdb01e9277d3d7de05f4cbfec73487f53287d685e0ac583e7efa9f8ddca796d0dae8d3d29fa46f35d282df4f509d89f607cb1daf8da1dc47e07f9ff29946fc837088d59ba4f6bea36294460f1db0145ec0ea7fb3b927edf8c263f47e5ae75d13b39a04f11b5a787442f4d27725180c36f8a657c27d15487be08ad038e532cef4169c27befbbc4f71e2c0adcf4be24211df7be4b7aef7d083fb8c3fcc71b8d63adac467ae707b4f50dcba27eb7663b18594aa07242f90cda08df26ff7533fd9eced547d7f40d12eeb15e72c4f10c07f1314d284f9499e60b72a32ca1fc517eec97b4fee02316c4db3be9f782e324116da6d582f716d410657c37fad8c960df21af009ee41de37b49eac7b01a903a546d0f947c5b1cfa0d9d65700f06e2da713f0ee70ef41b42edd5de31e6bcb791c17d070bda1b2fe489df2da2ddc67dc161dfe5e5409ebfaad3ee50413fec06f7a83886dca3e2e9c17b549a08ea1df71eff57ee5109d977ac03dde710b4fe237322c925dcb0effc549e059f9425b6d2527b688f10bff1c3ef7fd472fc469125e042e1d71445b897d87e2a58258b105fdc69c77c6d8293b41fddfd8132294c7427d1f71cadda9a34bdd53d9fbe7f6c86f1c0e037955c5c6390ee29bfd1d8efbbd0d4ef4ea7ef1d3fd5fa48fa5af071f9f6e6b35bc1e65f741f5c86df54659bf1bb1e41c8c779a33da2785a7bf3eeb7c16402afc1d61e614f688f389a643e817bbe275e549d61f49b35fc2e019e7d4f2b4ba26b36facd79f22d1c7e738ef5c3ba99a7d2efb2ceb9a7d2fa9da5df9ee377e9f8fdb9fe5dbaf6dda308763cc32c9785d1ef8ab5740d9fb6938848f76df86e37ac5d0e8b20ee69f07bf3a2b3727804cb1d2ea5e7011d87c0cf3f04724961a44a8036768693e8b0db30bf71908e554b79c677d3c23ef36d78875479b8ca4710f334bc476ad1a76a5804eb48867c66435bb878f27e20f9d48381d9a7aa9c5d4118bbf7c84238a92a9d42e2133a098ce97bb8e430869b1d461e7449e7e5b03056e679a69d9c1ae1fbaf4498332b1e84df7365f32ce28ea77aff84ee437a24da271c754eb4952ce707bf04d7194bba59a5ab8de5bc4e463555e077bbac037edba480ddbabf95eefbf86e127a91e21e1eae9f310dd72630d19b44625bdfcd7a9a1a5959249cf28989e14e629fca17e4a717108537e16f0ec7f791b213e495314df83de0f5ff4fc950a7c970e47f20c31129406548791864a8159814285f154de4c8ff5006adaf66315c8a48fba1237e3ee1a695c15a7004ce157c4af867f69452bf9d4b3f41427725d0bb14f01e05ecc3896b99501fc7feebb8b38c51788944f35d4147234fbf9d55364bd0e6f41c0ea3ecf49ba2f996a0f2909d49e1e7cd757879523502e3b951172647c371a29409746cf98bc3c8401e5b4117a13c5ef49bc01e70c3e208c5821a4827ca7689be2f083d53e6d9257c1e1dce6b73f47f49a26dcb13e08f1687c95231f155fb7b5056f4e3204f3694ae6acb5741df827c58837605f1fe06e5219ebe0349e103787f50374dfba2ff3aca81f3d25079a2c3af07e9f7f9069e6f81fd1bf2431f529777d3138cbaf926e26880f83b857ed40f9615cdeb3a2af6f3ffd25d14a1776743ea84f5f42d28c676efc57d69907197660f1bcb54b12348e72afc4eb85aea575d22f94d7c602486c7908e31be17a57efabdf72181fa09325741645306514d35441e5d43609eec53fb0363e8f7d7ebdee7aaa4ee6bf41dcef2ab6c95b4e3baf2e15562dd8f6bc5363a4fa4485fb0a1bb4d5260be469920cd57749eccbb0abf77f452bf9c617aacc925266be236933581de17275993e74ad6c41f4ad6d4b1c0cb92709f03dfb15a138f814d041b1ac6b06ad829560e279c03bf318579dd9a780e740ab2ed14f0fbe530d05f18e80fef583381fe24d09f04fa13417f229419aeedd967b0caa66efaeda9bf98a7eb01477a09679dceb05696e1611e0f5a4776d2ef32ad964eedfbe154e08b09ecd072d11751c609220769f15415bedba173e9c873ac2cd5b0caa4565176c5eb71cd3056c289bc6533ace93b59e513585b413a3c9f866139d745301d9e0e56c2bb45357c04de251684796f84327216a3b0699c2fe6f4559413cf1fa01faafdc6749db847d0a385d1d7890dd276039dcb12b4e79670e2cb0e5c97c5638c4aefb35876ce117f8cc172697993babf562f2affb856d330f9e34710357c0b513e904848375c3cc32b0b244d96805fd75b8d248be05fc5748b98af5a86f9be4bf3457ba88a15a2c29e0e572ed48e56c56241e14f8f53ced79a7c4468c1b651850cd6c776b7413f3937b41e4a66801dac43ec695a07900175856b25ab85e1e9fb4bf1a8886390ea6b3694ef8a24f88d34d720d2f27d110d5755579cce8b24fa77b63dd0bea2127e3a5215eda22fe2f4c7aa5843ac53dee595259276ff97e51c4fefb18236047b37923b184e9487712feb146b136aec561664114a38f0335e9045d0077e03ad9d09a883f61a07f94b8a08756da9c5be053ae81e0d724abe9bbadf0bb5aba63ff4fbed8c2a6648cac8d360ff8b255fd4e953e0c333280ff5b3e797800d725e97674d009ffa5010df8fdb5a6b21fdcb2cf68d6f95bdda4fe544f9415fddf47be417fdec37d7e7ec66dc3be6f60e3e4f19f63c761b3cefc47c30ac6cd7f2c2751dde0d669dd249e5a0df44437ba10cb4fcbdb4fc5e474338d513f2713c6aed19fb4787379cdee744c74c3294056b1825bc89702724fa8e16fd7a254a1ce88bd08617b8d92502de85604d3d867d01cf6c5cd5de3f0e791f99aaf9a7d6c437616c1f035fed5df0d9ce89d01f446b3283f647ba32c5de694ec43bd64a6742fbfdd2113f0fda5c0ac73aa06f6a9d1648405fd5fa9a3b01ed28fdfd86345df75fefe00ee2bbd9121eefabd14ef935d4e21880beb11ff3069dd572f1a5a0f796d19877b42b1074cfc63cdaa629f735d33ad1726e0d4ca379ef91a66139a1f79294572bdd3aecfda4b6476ba2dfb263de2fb5d0fd0c9acfe4401cbeb31b90f535690adee1806587e6064daeddd5b4aedb25fa5ec81a9764a5748a7b32a6d5ea2e597cdb25b0e3bb3b6839d543ca991e9884630f75af9d2738db3b443f31aae99430f41ed4028271777f39f81e5abf0f2fb9cca4f998d2587aa754d1d531f45e8246899ebdc1fb9c64c8548bd338899ee549dc46b82451c239c371186cd2ec32b03f65926356a9c0cd16456e162fe0d91d7fb109cf98413eaff2b8dec47526aeb1a1df02b50fdc0d60137618cfe25cc7bb97e83d4b3006b53584f3167a0753b27e372adecb940c7e3fdee3940af316f8fd328f6bd6dd4dd03719eafbd374422cf4ab339aefffce21eafbdf84beffe6d87fc9f7efd8fdded77dff85ef51dfff26f4fd374ffa7fc4f7ef88fd2bfafedf767f54e8bea8c13b1b047a37e895a964bf390edb79fe257d9d7409f26bc43d017c9765adf19bb0bddb3b5e787e0dd1d2e3fd67aa083a2bd375acad936ab43573ec05ba0681b5f437c775eed2e22e6bd5e29eedf9967cf5b8bbcf6b7163bbbe39aea06a71cf9ed5e22eeb749c15e8fd576af06eb00d411eef7fa8fab491447f5844aadef393e84f0324fa2cacb50373702f455247837d7cfa2a53b511d6cbffd9cf447363c891fffa8228cf0738bc274a1d43ef8560bf45de6dbabc4dbabc17f1fedfe865f576451c43e4822b77cbdfbf1254cd738847eaa1f7e2a17e6ddc15ea93daa457c0e7001f3e6a16bd77d38677d6f8fdf4de3bdf3d4d41b97b1c912b93d1a7e19517c7d0f942d9f8054bef5c29450af2edfc8254b95d4c55936baecd24e2fe2f1b3abd4dfd0e9744f7127d1bc7f4db36898c35e12a919d6903efb1be562f3a16d27eaeb7ef9ff5f6fd87a309fd516d6c2aff05bef16b02499a84771f8fee8f6e5a00e53d4d6cfff905ea15ead0c8447f7a92c84f0718b9e83f214e04897eff2a499904cfaf5e95201f1674c3501ff6f90bac3a3a5fabcbe60fb96f964978566fefa37a7bbf47fde3d35ed035de1d14b81bbfd652a36cc4638a64b8832682e722e509a03f93c4da9cb5849badf9d4559149b00e3d43acc54dfa993833f1457ed11fd28b4da825fa5cf54dfa59adebe7b0ae9f77868e35d5058035e2955b61bc69fde4b06c827c9c26a28f3f0ff54970ac359f6dc2b10675e0dd75cbf07ce803caf53a76e04edf81312bea7909bf061d6cd1de01da83a13d216acf7f5f779cde9d3b8beeaffc28f47e8fee435ea8e307ee7a049b6b2b6d80320f80bf857ea1307857a5200ede89e89c407d7d7ad74af3b2bf81bd2478df0abd0f5528be8e719470c1a4cdb1d25b78be07cbf0361232f45ca60df709a7b860ddd3a8dd9d3a9e6755b7c4ca0ff1b0664a20b616179e9d6cf706aeb3d0274c479676b3496963b00e53502e8c8b77d4799676b1741e68aa633d813ad6babf45bb6bf340d7f079419fb3607dab8d5d3c2724168b38c7d176db7fb656d35ff1f521ef37687de85e8a763f48fad0bd149a8f50a3a56f1ef30ad8cb0c47924ba0a78753033c5df356483ca82083f3ba38393710617d0bef31c3fdbcc620de2b86bf7d93a47e78ced0f28b605df3967f605f0ff3c3bb64f6394be633f399b4352473d4dcb12569da5d2778cb8aa6e9dc573629a9abb6fd69a31bef53b1dcf2cc5a6dd6cdfde5e40ef3147a0fed3e5ba9cb1efabe695469c9bc39e5e71e516eda4ef0fba5d07724e0974ef4b8123927ff67b7c3eb0a6e745fabd7be09d072c9ae7503554b5bf6966cc5fb449e2b65162ae65dacf6b4f455ed29ff6ac9d676d23c9e3ebd7917197a5f484ca610760bbfd17dd11bfa75ad5eab43bcbb5299e6801ac796fb5549b2800e2755a66f58b061b132ef02af8473ac1c9544e6545a934f46385e936608f35322db88e7c249f094cc33acc91f5acae72be6f7f1aedf198b9fb7ee6b4cc01be6c2a4d1d2f6c6dfbbb7faf03bafc7d6eafaaab8bfcbf13b8968b710e08d047833c14f5af66d9ebe59b8973b501ecb9ce4e7f525cabb12c9e45dca3f7972e33b1094b07068a3b05b14673ad3979835f1a3fa98798ed9614c5f92b2d3cdf2c7bbcf70f1fc8c94c82ae2f84e2959fb02131fd6be7167d43c87b73cd67773d1359fd8d5bbb0de9a1c463eaa079f6e866356d8d8ce174ae24bfc4b9c1b779eab9f0effbf53aff55d8476434c491a331ffa437a49ba6aaae0d03f9b568a5e7f0a5f6c87d6b790631b1e8a066f3d743a7c68fc5302c6571e158876ca5c6cd1521d8d21c7981aa6064ffb8b9fbf513af9e266e7602a22184b393a114b89736ec6affec663698367f927aebd9d94f5b677fcfcabbea58ad84d66a6452f3e6d17cadbf2f13612bd0e7acecd5f977f3ce63cd939781a7e68fc8eafc71f3b3c3edecfa3f7c367ab33b197e29d37cfb53369213da8a6cc517a2e3fc1bbfbc828cc0972593cec7e1e1841d63fd43db52913d32dda11926020ed8004240c25a8cedcb078ce2e5fec94aff0ed917586ab708e33ad7eb8f48437a6b50bc3a5afceb2467493ea1ceb58f87fe1a3c73e5968c5afab7e609dd04d3ef98135127eafb59ae1ff67622ebe95957ab1fab1d4d6b772623e7f6b554cf75beb63fa34b9c1df7e7c93b2b198ff5530781fa018d00178f8d7c1e001c0b3fb83c152c0ab80470109bf0d066f053a376f65eeea9c6c4bceba756bd6dd124e7e3c2fed9ec5e98f5956aeb6cccfcb2a5861f9c19aec1ca841884f99c8bb13a2ac2cb4accf5a5708bf0a73d6adcf2fbc71bcbc356bf2e1490561002c808b2584bf7b3f11002240029800618070c008c04888330a301a300610412ab8b140c7417a19e878a0130091f0db0c888aad2013013701a20131809b019300b1000be016c06480153005301510078807dc0ab80d300df01dc0ed80ef02a603be074800cc00cc04d80089805980d980244032e00ec09d8014402ae02ec01cc0dd003bc83a17e4bc07e8f781ce039a06743ed4351db000b010b008702fe03ec00f00f7439cff0064007e087800f4f020d087e0d962c863096029841f063c02f811847f0c7814f018e027c05b06c80464011e0728806c400e3c5f0ec805ac00ac04de1380270179805580d58035807c78be16b00e50002804de7ac0538022c0d3d0c2cf007e0af819e059c006c07f46ec223f0714034a001b010e4029a47102ca002ec86f13a01cb019b005b015f01ce079c00b00fcb70df062c22ea202b6c3ef1d809d0037a012b00bf27a095005a806fc02f05f80dd801ac0cb803d10ef15c0ab80bd805a401df05f03bc0ed807f825e057805f03de00fc06b01fe2bd09f82dc00338003808fc43002fe030e07780df038e00de02bc0df803a01e7014700cf047c09f00c7010d80138046c049c03b803f03fe02f82be06f80ff06fc1d700af02ee03dc0fb800f00a74186338026c087808f40431fc7ee22ff80df9f00fe09f814dae02cd066887f0e701ee003b4005a016d003fe002e022e033c025c065c0e7802f00ed800e4027e04bc0154017a01bd0030800ae02ae01ae03be02f402fa00fd80208060d7d5fe1ed4c8940c3dfc10fef7f47bc16005e035c05b80538016c055c0c8f783410b2011b000f028201fb01df02bc051c007800b805e40c407c1e0144032e03e4026a010b009500dd80f380ef81070197000f009809c867480544006201bf034602ba0067018f0574033a00bf0f01948f36130381e9007bf4d8062c076c0af0047011f002e007a01114d903fa01a900cb80f90092804ec071c076c027c08b80c88813489800ac8ff00e0af80cb00d34760bb010b00d98067019580a3804f005701e33f867a008aff01361ff016e0610c035e051c055c00f0c0b7005201d97adc038093805380984f4006c0b3801ac061c07d9f68cf4e01cdd67fe3f315ff040aa8007c08b8003c1ee814c0ab800380e3ffd4e23e3b244fa4519f827c804a4032c479f89f83cf6bf43242145101f10a014f038a019b00d88fd6af5e91b53a3b0f66343a29e9b312d8bc35eb722cd9eb57e5df69995a104ec8f7b3d617e458129e9e9a306376ded3b75bd232be3f1082c7645d4eee8fa726cccc7eecd6a905b759ee1a12f39b9e84d372967fd3e35ba72ebfed76cbb73ec61c4c8b4831b9119df90d7c9dda677cfbf32949dffebc79b5462fe8f4aa4ec91a8d46e8344aa7b7ea3441a7769d2ed0e9c33acdd4e9d38670b121bc49a7153addafd3c33a3da9d3533abd6a08c304a5d547a7b71ac2098670b2216c37841718c2193a7d58a72b0ce17c43f86943b8d810de64085718c29586708d21fc9a21bc3f7fb83e0f1bc2470de1938670c62a3d3f9dae3084f30de1a70de162437893215c6108571ac2353a7d4da7470de1933a3da5d30e43f8aa4e89de6fa3560f0f5b0ce15b0de1044338d910b6eb74814e330de1158670be21fcb4215c6c086f32842b0ce14a43b8c6107e4da7fb757ad2103e65087f68082f2f4cb0209d9f6129cc5995bf665dd6ba953905c89f61d19ecfd4a94da7893a9da5d3d93a4dd268c1407e05594f813906c3b7b2002c7201d52a3cd7f3cd1a8897b52e77fdaa9cd58505df5b9753b87edd6acb535979eb7342f1b342f1677e2dfed088cbb374f9b274f9b274f9b274f9b242f20de4f3e08de5d3f329d0f329d0f329d0f32908e593acd33b743a2341a733347d2487ca796850af96ef67e5e5e5aca3cff574857aba422ddd4f73d6adc1070bb2d6657fb768e53a901059645d96c582fc073415656567afcb2900990bf235fe838559ca9396fc3574f68370aece4fcf5bf37856dee083429dffd08a753959d943f8093aff46c2c21f7407fa1cba03a50509df5b8eabbbe14afcdef27559ab728688410af474c3e3c19a442f6ffefad54ae1ca35abbfbd17407c3d9fac99df946e7802e80e5afc449dced2e96c9d26e9f5987923f90a48819ebe404f5fa0a72fd0d31784d227ebf40e9dce48d0e90c4d5f7a3e857a3e857a3e857a3e2b966ae370bb4e8feaf4b24ea31ed6c7fb231a2dd4e98a1fe9e1c5df4e1f87667e92b687a697e559ebf30a2dcb730a951543c2796bb2b2873e2f2804b78986574217c8850eb4727541e1baf554e594bf6a654196be853098d9703ecd7448783053f26fbfe3df7ec7703fe0df7ec7f0f0ffe97e47f48f13526c3356a5596e9d9ab7fe36589bdd69f98ff5859635cb2dab7256ad59f74c384458050ba3071f79f0fb73efbb4f8b6f5bb57448fc82670a96e53cbdb2d0a2e03c956d79fc190b5ded4dcdcbb614ad2c5c816b2c589e6919dda0bcc5ab0bd6e7c3645788b6e99902cc05d3de69c94ab86b2aaef8b266e87426d25b4222fdeffd891ae10d6c61688065ff774b19fc93fe2791b97f39e6767dfec9d6e7995bf5f9ea039d7ffb8f75fea31acdd3e36dd5f9037f1bf47ea1d3ccff343cfffff91fa3d3d1b6bffd3271dcaab6cb9f0a64c4ffab12fdfbefdf7ffffefbf7dfbffffe4ffc8bf8b9367f86a8c540130cd46ea019069a69a0f9065a6ca015065a63a0fb0df4a8819e32d06603ed3050f87f188d30508b812618a8dd40330c34d340f30db4d8402b0cb4c640f71be851033d65a0cd06da61a0a464388d30508b812618a8dd40330c34d340f30db4d8402b0cb4c640f71be851033d65a0cd06da61a064e3701a61a016034d3050bb81661868a681e61b68b1815618688d81ee37d0a3067aca409b0db4c3408963388d30508b812618a8dd40330c34d340f30db4d8402b0cb4c640f71be851033d65a0cd06da61a0a474388d30508b812618a8dd40330c34d340f30db4d8402b0cb4c640f71be851033d65a0cd06da61a0c4399c4618a8c540130cd46ea019069a69a0f9065a6ca015065a63a0fb0df4a8819e32d06603ed305052369c4618a8c540130cd46ea019069a69a0f9065a6ca015065a63a0fb0df4a8819e32d06603ed3050e23250fd2f18d4de8b9f2cd2f8211ad4ff0819d863d07e3003e1642a4f053f2cbfd0dfff05896fca73' + +ISP_PROG = binascii.unhexlify(ISP_PROG) + +#print('ISP_FLASH progam size (compressed)', len(ISP_PROG)) +ISP_PROG = zlib.decompress(ISP_PROG) +#print('ISP_FLASH progam size (decompressed)', len(ISP_PROG)) + +def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█'): + """ + Call in a loop to create terminal progress bar + @params: + iteration - Required : current iteration (Int) + total - Required : total iterations (Int) + prefix - Optional : prefix string (Str) + suffix - Optional : suffix string (Str) + decimals - Optional : positive number of decimals in percent complete (Int) + length - Optional : character length of bar (Int) + fill - Optional : bar fill character (Str) + """ + percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) + filledLength = int(length * iteration // total) + bar = fill * filledLength + '-' * (length - filledLength) + print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = '\r') + # Print New Line on Complete + if iteration == total: + print() + +def slip_reader(port): + partial_packet = None + in_escape = False + + while True: + waiting = port.inWaiting() + read_bytes = port.read(1 if waiting == 0 else waiting) + if read_bytes == b'': + raise Exception("Timed out waiting for packet %s" % ("header" if partial_packet is None else "content")) + for b in read_bytes: + + if type(b) is int: + b = bytes([b]) # python 2/3 compat + + if partial_packet is None: # waiting for packet header + if b == b'\xc0': + partial_packet = b"" + else: + raise Exception('Invalid head of packet (%r)' % b) + elif in_escape: # part-way through escape sequence + in_escape = False + if b == b'\xdc': + partial_packet += b'\xc0' + elif b == b'\xdd': + partial_packet += b'\xdb' + else: + raise Exception('Invalid SLIP escape (%r%r)' % (b'\xdb', b)) + elif b == b'\xdb': # start of escape sequence + in_escape = True + elif b == b'\xc0': # end of packet + yield partial_packet + partial_packet = None + else: # normal byte in packet + partial_packet += b + + +class ISPResponse: + class ISPOperation(Enum): + ISP_ECHO = 0xC1 + ISP_NOP = 0xC2 + ISP_MEMORY_WRITE = 0xC3 + ISP_MEMORY_READ = 0xC4 + ISP_MEMORY_BOOT = 0xC5 + ISP_DEBUG_INFO = 0xD1 + + class ErrorCode(Enum): + ISP_RET_DEFAULT = 0 + ISP_RET_OK = 0xE0 + ISP_RET_BAD_DATA_LEN = 0xE1 + ISP_RET_BAD_DATA_CHECKSUM = 0xE2 + ISP_RET_INVALID_COMMAND = 0xE3 + + @staticmethod + def parse(data): + op = data[0] + reason = data[1] + text = '' + try: + if ISPResponse.ISPOperation(op) == ISPResponse.ISPOperation.ISP_DEBUG_INFO: + text = data[2:].decode() + except ValueError: + print('Warning: recv unknown op', op) + + return (op, reason, text) + + +class FlashModeResponse: + class Operation(Enum): + ISP_DEBUG_INFO = 0xD1 + ISP_NOP = 0xD2 + ISP_FLASH_ERASE = 0xD3 + ISP_FLASH_WRITE = 0xD4 + ISP_REBOOT = 0xD5 + ISP_UARTHS_BAUDRATE_SET = 0xD6 + FLASHMODE_FLASH_INIT = 0xD7 + + class ErrorCode(Enum): + ISP_RET_DEFAULT = 0 + ISP_RET_OK = 0xE0 + ISP_RET_BAD_DATA_LEN = 0xE1 + ISP_RET_BAD_DATA_CHECKSUM = 0xE2 + ISP_RET_INVALID_COMMAND = 0xE3 + + @staticmethod + def parse(data): + op = data[0] + reason = data[1] + text = '' + if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_DEBUG_INFO: + text = data[2:].decode() + + return (op, reason, text) + + +def chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in range(0, len(l), n): + yield l[i:i + n] + + +class MAIXLoader: + def change_baudrate(self, baudrate): + print(INFO_MSG,"Selected Baudrate: ", baudrate, BASH_TIPS['DEFAULT']) + out = struct.pack('III', 0, 4, baudrate) + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + out = struct.pack('HH', 0xd6, 0x00) + crc32_checksum + out + self.write(out) + time.sleep(0.05) + self._port.baudrate = baudrate + + def __init__(self, port='/dev/ttyUSB1', baudrate=115200): + # configure the serial connections (the parameters differs on the device you are connecting to) + self._port = serial.Serial( + port=port, + baudrate=baudrate, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=0.1 + ) + print(INFO_MSG, "Default baudrate is", baudrate, ", later it may be changed to the value you set.", BASH_TIPS['DEFAULT']) + + self._port.isOpen() + self._slip_reader = slip_reader(self._port) + + """ Read a SLIP packet from the serial port """ + + def read(self): + return next(self._slip_reader) + + """ Write bytes to the serial port while performing SLIP escaping """ + + def write(self, packet): + buf = b'\xc0' \ + + (packet.replace(b'\xdb', b'\xdb\xdd').replace(b'\xc0', b'\xdb\xdc')) \ + + b'\xc0' + #print('[WRITE]', binascii.hexlify(buf)) + return self._port.write(buf) + + def read_loop(self): + out = b'' + # while self._port.inWaiting() > 0: + # out += self._port.read(1) + + # print(out) + while 1: + sys.stdout.write('[RECV] raw data: ') + sys.stdout.write(binascii.hexlify(self._port.read(1)).decode()) + sys.stdout.flush() + + def recv_one_return(self): + timeout_init = time.time() + data = b'' + # find start boarder + #sys.stdout.write('[RECV one return] raw data: ') + while 1: + if time.time() - timeout_init > timeout: + raise TimeoutError + c = self._port.read(1) + #sys.stdout.write(binascii.hexlify(c).decode()) + sys.stdout.flush() + if c == b'\xc0': + break + + in_escape = False + while 1: + if time.time() - timeout_init > timeout: + raise TimeoutError + c = self._port.read(1) + #sys.stdout.write(binascii.hexlify(c).decode()) + sys.stdout.flush() + if c == b'\xc0': + break + + elif in_escape: # part-way through escape sequence + in_escape = False + if c == b'\xdc': + data += b'\xc0' + elif c == b'\xdd': + data += b'\xdb' + else: + raise Exception('Invalid SLIP escape (%r%r)' % (b'\xdb', b)) + elif c == b'\xdb': # start of escape sequence + in_escape = True + + data += c + + #sys.stdout.write('\n') + return data + + def reset_to_isp_kd233(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.01) + #print('-- RESET to LOW, IO16 to HIGH --') + # Pull reset down and keep 10ms + self._port.setDTR (True) + self._port.setRTS (False) + time.sleep(0.01) + #print('-- IO16 to LOW, RESET to HIGH --') + # Pull IO16 to low and release reset + self._port.setRTS (True) + self._port.setDTR (False) + time.sleep(0.01) + + def reset_to_isp_dan(self): + self._port.dtr = False + self._port.rts = False + time.sleep(0.01) + #print('-- RESET to LOW, IO16 to HIGH --') + # Pull reset down and keep 10ms + self._port.dtr = False + self._port.rts = True + time.sleep(0.01) + #print('-- IO16 to LOW, RESET to HIGH --') + # Pull IO16 to low and release reset + self._port.rts = False + self._port.dtr = True + time.sleep(0.01) + + def reset_to_boot(self): + self._port.setDTR (False) + self._port.setRTS (False) + time.sleep(0.01) + #print('-- RESET to LOW --') + # Pull reset down and keep 10ms + self._port.setRTS (False) + self._port.setDTR (True) + time.sleep(0.01) + #print('-- RESET to HIGH, BOOT --') + # Pull IO16 to low and release reset + self._port.setRTS (False) + self._port.setDTR (False) + time.sleep(0.01) + + def greeting(self): + self._port.write(b'\xc0\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') + op, reason, text = ISPResponse.parse(self.recv_one_return()) + + #print('MAIX return op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name) + + + def flash_greeting(self): + retry_count = 0 + while 1: + self._port.write(b'\xc0\xd2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') + retry_count = retry_count + 1 + try: + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + except IndexError: + if retry_count > MAX_RETRY_TIMES: + print(ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) + sys.exit(1) + time.sleep(0.1) + continue + print(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT']) + #print('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_NOP: + print(INFO_MSG,"Boot to Flashmode Successfully",BASH_TIPS['DEFAULT']) + break + else: + if retry_count > MAX_RETRY_TIMES: + print(ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) + sys.exit(1) + print(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT']) + time.sleep(0.1) + continue + + def boot(self, address=0x80000000): + print(INFO_MSG,"Booting From " + hex(address),BASH_TIPS['DEFAULT']) + + out = struct.pack('II', address, 0) + + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xc5, 0x00) + crc32_checksum + out # op: ISP_MEMORY_WRITE: 0xc3 + self.write(out) + + def recv_debug(self): + op, reason, text = ISPResponse.parse(self.recv_one_return()) + #print('[RECV] op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name) + if text: + print('-' * 30) + print(text) + print('-' * 30) + if ISPResponse.ErrorCode(reason) not in (ISPResponse.ErrorCode.ISP_RET_DEFAULT, ISPResponse.ErrorCode.ISP_RET_OK): + print('Failed, retry, errcode=', hex(reason)) + return False + return True + + def flash_recv_debug(self): + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + #print('[Flash-RECV] op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + if text: + print('-' * 30) + print(text) + print('-' * 30) + + if FlashModeResponse.ErrorCode(reason) not in (FlashModeResponse.ErrorCode.ISP_RET_OK, FlashModeResponse.ErrorCode.ISP_RET_OK): + print('Failed, retry') + return False + return True + + def init_flash(self, chip_type): + chip_type = int(chip_type) + print(INFO_MSG,"Selected Flash: ",("In-Chip", "On-Board")[chip_type],BASH_TIPS['DEFAULT']) + out = struct.pack('II', chip_type, 0) + crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xd7, 0x00) + crc32_checksum + out + + sent = self.write(out) + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + #print('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + + def flash_dataframe(self, data, address=0x80000000): + DATAFRAME_SIZE = 1024 + data_chunks = chunks(data, DATAFRAME_SIZE) + #print('[DEBUG] flash dataframe | data length:', len(data)) + total_chunk = math.ceil(len(data)/DATAFRAME_SIZE) + + for n, chunk in enumerate(data_chunks): + while 1: + #print('[INFO] sending chunk', i, '@address', hex(address), 'chunklen', len(chunk)) + out = struct.pack('II', address, len(chunk)) + + crc32_checksum = struct.pack('I', binascii.crc32(out + chunk) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xc3, 0x00) + crc32_checksum + out + chunk # op: ISP_MEMORY_WRITE: 0xc3 + sent = self.write(out) + #print('[INFO]', 'sent', sent, 'bytes', 'checksum', binascii.hexlify(crc32_checksum).decode()) + + address += len(chunk) + + if self.recv_debug(): + break + printProgressBar(n+1, total_chunk, prefix = 'Downloading ISP:', suffix = 'Complete', length = 50) + + def dump_to_flash(self, data, address=0): + ''' + typedef struct __attribute__((packed)) { + uint8_t op; + int32_t checksum; // 下面的所有字段都要参与checksum的计算 + uint32_t address; + uint32_t data_len; + uint8_t data_buf[1024]; + } isp_request_t; + ''' + + DATAFRAME_SIZE = 4096 + data_chunks = chunks(data, DATAFRAME_SIZE) + #print('[DEBUG] flash dataframe | data length:', len(data)) + + + + for n, chunk in enumerate(data_chunks): + #print('[INFO] sending chunk', i, '@address', hex(address)) + out = struct.pack('II', address, len(chunk)) + + crc32_checksum = struct.pack('I', binascii.crc32(out + chunk) & 0xFFFFFFFF) + + out = struct.pack('HH', 0xd4, 0x00) + crc32_checksum + out + chunk + #print("[$$$$]", binascii.hexlify(out[:32]).decode()) + retry_count = 0 + while True: + try: + sent = self.write(out) + #print('[INFO]', 'sent', sent, 'bytes', 'checksum', crc32_checksum) + self.flash_recv_debug() + except: + retry_count = retry_count + 1 + if retry_count > MAX_RETRY_TIMES: + print(ERROR_MSG,"Error Count Exceeded, Stop Trying",BASH_TIPS['DEFAULT']) + sys.exit(1) + continue + break + address += len(chunk) + + + + def flash_erase(self): + #print('[DEBUG] erasing spi flash.') + self._port.write(b'\xc0\xd3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') + op, reason, text = FlashModeResponse.parse(self.recv_one_return()) + #print('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', + # FlashModeResponse.ErrorCode(reason).name) + + def install_flash_bootloader(self, data): + # 1. 刷入 flash bootloader + self.flash_dataframe(data, address=0x80000000) + + def flash_firmware(self, firmware_bin: bytes, aes_key: bytes = None, address_offset = 0, sha256Prefix = True): + #print('[DEBUG] flash_firmware DEBUG: aeskey=', aes_key) + + if sha256Prefix == True: + # 固件加上头 + # 格式: SHA256(after)(32bytes) + AES_CIPHER_FLAG (1byte) + firmware_size(4bytes) + firmware_data + aes_cipher_flag = b'\x01' if aes_key else b'\x00' + + # 加密 + if aes_key: + enc = AES_128_CBC(aes_key, iv=b'\x00'*16).encrypt + padded = firmware_bin + b'\x00'*15 # zero pad + firmware_bin = b''.join([enc(padded[i*16:i*16+16]) for i in range(len(padded)//16)]) + + firmware_len = len(firmware_bin) + + data = aes_cipher_flag + struct.pack('I', firmware_len) + firmware_bin + + sha256_hash = hashlib.sha256(data).digest() + + firmware_with_header = data + sha256_hash + + total_chunk = math.ceil(len(firmware_with_header)/4096) + # 3. 分片刷入固件 + data_chunks = chunks(firmware_with_header, 4096) # 4kb for a sector + else: + total_chunk = math.ceil(len(firmware_bin)/4096) + data_chunks = chunks(firmware_bin, 4096) + + for n, chunk in enumerate(data_chunks): + chunk = chunk.ljust(4096, b'\x00') # align by 4kb + + # 3.1 刷入一个dataframe + #print('[INFO]', 'Write firmware data piece') + self.dump_to_flash(chunk, address= n * 4096 + address_offset) + printProgressBar(n+1, total_chunk, prefix = 'Downloading:', suffix = 'Complete', length = 50) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="COM Port", default="DEFAULT") + parser.add_argument("-c", "--chip", help="SPI Flash type, 1 for in-chip, 0 for on-board", default=1) + parser.add_argument("-b", "--baudrate", type=int, help="UART baudrate for uploading firmware", default=115200) + parser.add_argument("-l", "--bootloader", help="bootloader bin path", required=False, default=None) + parser.add_argument("-k", "--key", help="AES key in hex, if you need encrypt your firmware.", required=False, default=None) + parser.add_argument("-v", "--verbose", help="increase output verbosity", default=False, + action="store_true") + parser.add_argument("-t", "--terminal", help="Start a terminal after finish", default=False, action="store_true") + parser.add_argument("firmware", help="firmware bin path") + + args = parser.parse_args() + if args.port == "DEFAULT": + try: + list_port_info = next(serial.tools.list_ports.grep(VID_LIST_FOR_AUTO_LOOKUP)) #Take the first one within the list + print(INFO_MSG,"COM Port Auto Detected, Selected ",list_port_info.device,BASH_TIPS['DEFAULT']) + _port = list_port_info.device + except StopIteration: + print(ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) + sys.exit(1) + else: + _port = args.port + print(INFO_MSG,"COM Port Selected Manually: ",_port,BASH_TIPS['DEFAULT']) + + loader = MAIXLoader(port=_port, baudrate=115200) + + + # 1. Greeting. + print(INFO_MSG,"Trying to Enter the ISP Mode...",BASH_TIPS['DEFAULT']) + + retry_count = 0 + + while 1: + retry_count = retry_count + 1 + if retry_count > 15: + print("\n" + ERROR_MSG,"No vaild Kendryte K210 found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`-p '+('/dev/ttyUSB0', 'COM3')[sys.platform == 'win32']+'`',BASH_TIPS['DEFAULT']) + sys.exit(1) + try: + print('.', end='') + loader.reset_to_isp_dan() + loader.greeting() + break + except TimeoutError: + pass + + try: + print('_', end='') + loader.reset_to_isp_kd233() + loader.greeting() + break + except TimeoutError: + pass + timeout = 3 + print() + print(INFO_MSG,"Greeting Message Detected, Start Downloading ISP",BASH_TIPS['DEFAULT']) + # 2. flash bootloader and firmware + try: + firmware_bin = open(args.firmware, 'rb') + except FileNotFoundError: + print(ERROR_MSG,'Unable to find the firmware at ', args.firmware, BASH_TIPS['DEFAULT']) + sys.exit(1) + + # install bootloader at 0x80000000 + if args.bootloader: + loader.install_flash_bootloader(open(args.bootloader, 'rb').read()) + else: + loader.install_flash_bootloader(ISP_PROG) + + loader.boot() + + print(INFO_MSG,"Wait For 0.3 second for ISP to Boot", BASH_TIPS['DEFAULT']) + + time.sleep(0.3) + + loader.flash_greeting() + + if args.baudrate != 115200: + loader.change_baudrate(args.baudrate) + + loader.init_flash(args.chip) + + if ".kfpkg" == os.path.splitext(args.firmware)[1]: + print(INFO_MSG,"Extracting KFPKG ... ", BASH_TIPS['DEFAULT']) + firmware_bin.close() + with tempfile.TemporaryDirectory() as tmpdir: + try: + with zipfile.ZipFile(args.firmware) as zf: + zf.extractall(tmpdir) + except zipfile.BadZipFile: + print(ERROR_MSG,'Unable to Decompress the kfpkg, your file might be corrupted.',BASH_TIPS['DEFAULT']) + sys.exit(1) + + fFlashList = open(os.path.join(tmpdir, 'flash-list.json'), "r") + sFlashList = re.sub(r'"address": (.*),', r'"address": "\1",', fFlashList.read()) #Pack the Hex Number in json into str + fFlashList.close() + jsonFlashList = json.loads(sFlashList) + for lBinFiles in jsonFlashList['files']: + print(INFO_MSG,"Writing",lBinFiles['bin'],"into","0x%08x"%int(lBinFiles['address'], 0),BASH_TIPS['DEFAULT']) + firmware_bin = open(os.path.join(tmpdir, lBinFiles["bin"]), "rb") + loader.flash_firmware(firmware_bin.read(), None, int(lBinFiles['address'], 0), lBinFiles['sha256Prefix']) + firmware_bin.close() + else: + if args.key: + aes_key = binascii.a2b_hex(args.key) + if len(aes_key) != 16: + raise ValueError('AES key must by 16 bytes') + + loader.flash_firmware(firmware_bin.read(), aes_key=aes_key) + else: + loader.flash_firmware(firmware_bin.read()) + + # 3. boot + loader.reset_to_boot() + print(INFO_MSG,"Rebooting...", BASH_TIPS['DEFAULT']) + loader._port.close() + + if(args.terminal == True): + import serial.tools.miniterm + sys.argv = [''] + serial.tools.miniterm.main(default_port=_port, default_baudrate=115200, default_dtr=False, default_rts=False) diff --git a/tools/opensbi/README.md b/tools/opensbi/README.md index 79ad945a..a992b69d 100644 --- a/tools/opensbi/README.md +++ b/tools/opensbi/README.md @@ -2,8 +2,12 @@ These are binary release of OpenSBI on this [commit](https://github.com/riscv/opensbi/tree/194dbbe5a13dff2255411c26d249f3ad4ef42c0b) at 2019.04.15. -- fu540.elf: opensbi-0.3-rv64-bin/platform/sifive/fu540/firmware/fw_jump.elf - virt_rv32.elf: opensbi-0.3-rv32-bin/platform/qemu/virt/firmware/fw_jump.elf - virt_rv64.elf: opensbi-0.3-rv64-bin/platform/qemu/virt/firmware/fw_jump.elf -NOTE: The [official v0.3 release](https://github.com/riscv/opensbi/releases/tag/v0.3) has bug on serial interrupt. +NOTE: The [official v0.3 release](https://github.com/riscv/opensbi/releases/tag/v0.3) has bug on serial interrupt. Also, Rocket-Chip based CPUs (including SiFive Unleashed) seem to have unintended behavior on + +For K210 & SiFive Unleashed: It needs some modification. The binary is from this [commit](https://github.com/rcore-os/opensbi/commit/a9638d092756975ceb50073d736a17cef439c7b6). + +* k210.elf: build/platform/kendryte/k210/firmware/fw_payload.elf +* fu540.elf: build/platform/sifive/fu540/firmware/fw_jump.elf diff --git a/tools/opensbi/fu540.elf b/tools/opensbi/fu540.elf index 4b76fae3..5aeeb118 100755 Binary files a/tools/opensbi/fu540.elf and b/tools/opensbi/fu540.elf differ diff --git a/tools/opensbi/k210.elf b/tools/opensbi/k210.elf new file mode 100755 index 00000000..221c37ee Binary files /dev/null and b/tools/opensbi/k210.elf differ diff --git a/user b/user index 8dbc0edb..bb73d6ec 160000 --- a/user +++ b/user @@ -1 +1 @@ -Subproject commit 8dbc0edb935a62d748aaac39258d4a985de0ae17 +Subproject commit bb73d6ecce1ab0e6fae692c51e4335772b0335d4