mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-18 03:56:42 +00:00
add hello_c support
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,2 +1,3 @@
|
|||||||
src/kxos-user/hello_world/hello_world filter=lfs diff=lfs merge=lfs -text
|
src/kxos-user/hello_world/hello_world filter=lfs diff=lfs merge=lfs -text
|
||||||
src/kxos-user/fork/fork filter=lfs diff=lfs merge=lfs -text
|
src/kxos-user/fork/fork filter=lfs diff=lfs merge=lfs -text
|
||||||
|
src/kxos-user/hello_c/hello filter=lfs diff=lfs merge=lfs -text
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
runner = "cargo run --package kxos-boot --"
|
runner = "cargo run --package kxos-boot --"
|
||||||
|
|
||||||
[alias]
|
[alias]
|
||||||
|
kcheck = "check --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||||
kbuild = "build --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
kbuild = "build --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||||
kimage = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -- --no-run"
|
kimage = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -- --no-run"
|
||||||
krun = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
krun = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||||
|
@ -181,6 +181,19 @@ impl MemorySet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// determine whether a virtaddr is in a mapped area
|
||||||
|
pub fn is_mapped(&self, vaddr: VirtAddr) -> bool {
|
||||||
|
for (start_address, map_area) in self.areas.iter() {
|
||||||
|
if *start_address > vaddr {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if *start_address <= vaddr && vaddr < *start_address + map_area.mapped_size() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pt: PageTable::new(),
|
pt: PageTable::new(),
|
||||||
|
@ -125,27 +125,28 @@ impl VmIo for VmFrameVec {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&mut self, offset: usize, buf: &[u8]) -> Result<()> {
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
let mut start = offset;
|
todo!()
|
||||||
let mut remain = buf.len();
|
// let mut start = offset;
|
||||||
let mut processed = 0;
|
// let mut remain = buf.len();
|
||||||
for pa in self.0.iter_mut() {
|
// let mut processed = 0;
|
||||||
if start >= PAGE_SIZE {
|
// for pa in self.0.iter_mut() {
|
||||||
start -= PAGE_SIZE;
|
// if start >= PAGE_SIZE {
|
||||||
} else {
|
// start -= PAGE_SIZE;
|
||||||
let copy_len = (PAGE_SIZE - start).min(remain);
|
// } else {
|
||||||
let src = &buf[processed..processed + copy_len];
|
// let copy_len = (PAGE_SIZE - start).min(remain);
|
||||||
let dst = &mut pa.start_pa().kvaddr().get_bytes_array()[start..src.len() + start];
|
// let src = &buf[processed..processed + copy_len];
|
||||||
dst.copy_from_slice(src);
|
// let dst = &mut pa.start_pa().kvaddr().get_bytes_array()[start..src.len() + start];
|
||||||
processed += copy_len;
|
// dst.copy_from_slice(src);
|
||||||
remain -= copy_len;
|
// processed += copy_len;
|
||||||
start = 0;
|
// remain -= copy_len;
|
||||||
if remain == 0 {
|
// start = 0;
|
||||||
break;
|
// if remain == 0 {
|
||||||
}
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
Ok(())
|
// }
|
||||||
|
// Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ pub trait VmIo: Send + Sync {
|
|||||||
/// On success, the input `buf` must be written to the VM object entirely.
|
/// On success, the input `buf` must be written to the VM object entirely.
|
||||||
/// If, for any reason, the input data can only be written partially,
|
/// If, for any reason, the input data can only be written partially,
|
||||||
/// then the method shall return an error.
|
/// then the method shall return an error.
|
||||||
fn write_bytes(&mut self, offset: usize, buf: &[u8]) -> Result<()>;
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()>;
|
||||||
|
|
||||||
/// Write a value of a specified type at a specified offset.
|
/// Write a value of a specified type at a specified offset.
|
||||||
fn write_val<T: Pod>(&mut self, offset: usize, new_val: &T) -> Result<()> {
|
fn write_val<T: Pod>(&mut self, offset: usize, new_val: &T) -> Result<()> {
|
||||||
|
@ -64,6 +64,12 @@ impl VmSpace {
|
|||||||
Ok(options.addr.unwrap())
|
Ok(options.addr.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// determine whether a vaddr is already mapped
|
||||||
|
pub fn is_mapped(&self, vaddr: Vaddr) -> bool {
|
||||||
|
let memory_set = self.memory_set.exclusive_access();
|
||||||
|
memory_set.is_mapped(VirtAddr(vaddr))
|
||||||
|
}
|
||||||
|
|
||||||
/// Unmaps the physical memory pages within the VM address range.
|
/// Unmaps the physical memory pages within the VM address range.
|
||||||
///
|
///
|
||||||
/// The range is allowed to contain gaps, where no physical memory pages
|
/// The range is allowed to contain gaps, where no physical memory pages
|
||||||
@ -112,7 +118,7 @@ impl VmIo for VmSpace {
|
|||||||
self.memory_set.exclusive_access().read_bytes(vaddr, buf)
|
self.memory_set.exclusive_access().read_bytes(vaddr, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&mut self, vaddr: usize, buf: &[u8]) -> Result<()> {
|
fn write_bytes(&self, vaddr: usize, buf: &[u8]) -> Result<()> {
|
||||||
self.memory_set.exclusive_access().write_bytes(vaddr, buf)
|
self.memory_set.exclusive_access().write_bytes(vaddr, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,10 @@ pub fn init_process() {
|
|||||||
process.pid()
|
process.pid()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let hello_c_content = read_hello_c_content();
|
||||||
|
let process = Process::spawn_user_process(hello_c_content);
|
||||||
|
info!("spawn hello_c process, pid = {}", process.pid());
|
||||||
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,3 +70,7 @@ pub fn read_hello_world_content() -> &'static [u8] {
|
|||||||
fn read_fork_content() -> &'static [u8] {
|
fn read_fork_content() -> &'static [u8] {
|
||||||
include_bytes!("../../kxos-user/fork/fork")
|
include_bytes!("../../kxos-user/fork/fork")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_hello_c_content() -> &'static [u8] {
|
||||||
|
include_bytes!("../../kxos-user/hello_c/hello")
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ use alloc::vec::Vec;
|
|||||||
use kxos_frame::{
|
use kxos_frame::{
|
||||||
config::PAGE_SIZE,
|
config::PAGE_SIZE,
|
||||||
debug,
|
debug,
|
||||||
vm::{Vaddr, VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace},
|
vm::{Vaddr, VmIo, VmPerm, VmSpace},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use xmas_elf::{
|
use xmas_elf::{
|
||||||
@ -80,22 +80,32 @@ impl<'a> ElfSegment<'a> {
|
|||||||
self.range.end
|
self.range.end
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_and_map(&self, vm_space: &VmSpace) -> Result<(), ElfError> {
|
fn copy_segment(&self, vm_space: &VmSpace) -> Result<(), ElfError> {
|
||||||
if !self.is_page_aligned() {
|
// if !self.is_page_aligned() {
|
||||||
return Err(ElfError::SegmentNotPageAligned);
|
// return Err(ElfError::SegmentNotPageAligned);
|
||||||
}
|
// }
|
||||||
|
// map page
|
||||||
|
debug!(
|
||||||
|
"map_segment: 0x{:x} - 0x{:x}",
|
||||||
|
self.start_address(),
|
||||||
|
self.end_address()
|
||||||
|
);
|
||||||
let vm_page_range = VmPageRange::new_range(self.start_address()..self.end_address());
|
let vm_page_range = VmPageRange::new_range(self.start_address()..self.end_address());
|
||||||
let page_number = vm_page_range.len();
|
for page in vm_page_range.iter() {
|
||||||
// allocate frames
|
// map page if the page is not mapped
|
||||||
let vm_alloc_options = VmAllocOptions::new(page_number);
|
if !page.is_mapped(vm_space) {
|
||||||
let mut frames = VmFrameVec::allocate(&vm_alloc_options)?;
|
let vm_perm = self.vm_perm | VmPerm::W;
|
||||||
|
page.map_page(vm_space, vm_perm)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"copy_segment: 0x{:x} - 0x{:x}",
|
||||||
|
self.start_address(),
|
||||||
|
self.end_address()
|
||||||
|
);
|
||||||
// copy segment
|
// copy segment
|
||||||
frames.write_bytes(0, self.data)?;
|
vm_space.write_bytes(self.start_address(), self.data)?;
|
||||||
// map segment
|
|
||||||
let mut vm_map_options = VmMapOptions::new();
|
|
||||||
vm_map_options.addr(Some(self.start_address()));
|
|
||||||
vm_map_options.perm(self.vm_perm);
|
|
||||||
vm_space.map(frames, &vm_map_options)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,13 +170,9 @@ impl<'a> ElfLoadInfo<'a> {
|
|||||||
Ok(VmPageRange::new_range(elf_start_address..elf_end_address))
|
Ok(VmPageRange::new_range(elf_start_address..elf_end_address))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_and_map(&self, vm_space: &VmSpace) -> Result<(), ElfError> {
|
pub fn copy_data(&self, vm_space: &VmSpace) -> Result<(), ElfError> {
|
||||||
for segment in self.segments.iter() {
|
for segment in self.segments.iter() {
|
||||||
debug!(
|
segment.copy_segment(vm_space)?;
|
||||||
"map segment: 0x{:x}-0x{:x}",
|
|
||||||
segment.range.start, segment.range.end
|
|
||||||
);
|
|
||||||
segment.copy_and_map(vm_space)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -217,10 +223,10 @@ fn check_elf_header(elf_file: &ElfFile) -> Result<(), ElfError> {
|
|||||||
return Err(ElfError::UnsupportedElfType);
|
return Err(ElfError::UnsupportedElfType);
|
||||||
}
|
}
|
||||||
// system V ABI
|
// system V ABI
|
||||||
debug_assert_eq!(elf_header.pt1.os_abi(), header::OsAbi::SystemV);
|
// debug_assert_eq!(elf_header.pt1.os_abi(), header::OsAbi::SystemV);
|
||||||
if elf_header.pt1.os_abi() != header::OsAbi::SystemV {
|
// if elf_header.pt1.os_abi() != header::OsAbi::SystemV {
|
||||||
return Err(ElfError::UnsupportedElfType);
|
// return Err(ElfError::UnsupportedElfType);
|
||||||
}
|
// }
|
||||||
// x86_64 architecture
|
// x86_64 architecture
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
elf_header.pt2.machine().as_machine(),
|
elf_header.pt2.machine().as_machine(),
|
||||||
|
@ -15,7 +15,9 @@ pub fn load_elf_to_vm_space<'a>(
|
|||||||
vm_space: &VmSpace,
|
vm_space: &VmSpace,
|
||||||
) -> Result<ElfLoadInfo<'a>, ElfError> {
|
) -> Result<ElfLoadInfo<'a>, ElfError> {
|
||||||
let elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content)?;
|
let elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content)?;
|
||||||
elf_load_info.copy_and_map(vm_space)?;
|
debug!("parse data success");
|
||||||
|
elf_load_info.copy_data(vm_space)?;
|
||||||
|
debug!("copy_data success");
|
||||||
elf_load_info.debug_check_map_result(vm_space);
|
elf_load_info.debug_check_map_result(vm_space);
|
||||||
debug!("map elf success");
|
debug!("map elf success");
|
||||||
elf_load_info.map_and_clear_user_stack(vm_space);
|
elf_load_info.map_and_clear_user_stack(vm_space);
|
||||||
|
@ -7,14 +7,15 @@ use kxos_frame::{
|
|||||||
vm::{Vaddr, VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace},
|
vm::{Vaddr, VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::elf::ElfError;
|
||||||
|
|
||||||
/// A set of **CONTINUOUS** virtual pages in VmSpace
|
/// A set of **CONTINUOUS** virtual pages in VmSpace
|
||||||
pub struct VmPageRange<'a> {
|
pub struct VmPageRange {
|
||||||
start_page: VmPage,
|
start_page: VmPage,
|
||||||
end_page: VmPage,
|
end_page: VmPage,
|
||||||
vm_space: Option<&'a VmSpace>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VmPageRange<'a> {
|
impl VmPageRange {
|
||||||
/// create a set of pages containing virtual address range [a, b)
|
/// create a set of pages containing virtual address range [a, b)
|
||||||
pub const fn new_range(vaddr_range: Range<Vaddr>) -> Self {
|
pub const fn new_range(vaddr_range: Range<Vaddr>) -> Self {
|
||||||
let start_page = VmPage::containing_address(vaddr_range.start);
|
let start_page = VmPage::containing_address(vaddr_range.start);
|
||||||
@ -22,7 +23,6 @@ impl<'a> VmPageRange<'a> {
|
|||||||
Self {
|
Self {
|
||||||
start_page,
|
start_page,
|
||||||
end_page,
|
end_page,
|
||||||
vm_space: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,6 @@ impl<'a> VmPageRange<'a> {
|
|||||||
Self {
|
Self {
|
||||||
start_page: page,
|
start_page: page,
|
||||||
end_page: page,
|
end_page: page,
|
||||||
vm_space: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,45 +45,47 @@ impl<'a> VmPageRange<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// allocate a set of physical frames and map self to frames
|
/// allocate a set of physical frames and map self to frames
|
||||||
pub fn map(&mut self, vm_space: &'a VmSpace, vm_perm: VmPerm) {
|
pub fn map(&mut self, vm_space: &VmSpace, vm_perm: VmPerm) {
|
||||||
let options = VmAllocOptions::new(self.len());
|
let options = VmAllocOptions::new(self.len());
|
||||||
let frames = VmFrameVec::allocate(&options).expect("allocate frame error");
|
let frames = VmFrameVec::allocate(&options).expect("allocate frame error");
|
||||||
self.map_to(vm_space, frames, vm_perm);
|
self.map_to(vm_space, frames, vm_perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// map self to a set of zeroed frames
|
/// map self to a set of zeroed frames
|
||||||
pub fn map_zeroed(&mut self, vm_space: &'a VmSpace, vm_perm: VmPerm) {
|
pub fn map_zeroed(&mut self, vm_space: &VmSpace, vm_perm: VmPerm) {
|
||||||
let options = VmAllocOptions::new(self.len());
|
let options = VmAllocOptions::new(self.len());
|
||||||
let mut frames = VmFrameVec::allocate(&options).expect("allocate frame error");
|
let frames = VmFrameVec::allocate(&options).expect("allocate frame error");
|
||||||
let buffer = vec![0u8; self.nbytes()];
|
let buffer = vec![0u8; self.nbytes()];
|
||||||
frames.write_bytes(0, &buffer).expect("write zero failed");
|
self.map_to(vm_space, frames, vm_perm);
|
||||||
self.map_to(vm_space, frames, vm_perm)
|
vm_space
|
||||||
|
.write_bytes(self.start_address(), &buffer)
|
||||||
|
.expect("write zero failed");
|
||||||
|
// frames.write_bytes(0, &buffer).expect("write zero failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// map self to a set of frames
|
/// map self to a set of frames
|
||||||
pub fn map_to(&mut self, vm_space: &'a VmSpace, frames: VmFrameVec, vm_perm: VmPerm) {
|
pub fn map_to(&mut self, vm_space: &VmSpace, frames: VmFrameVec, vm_perm: VmPerm) {
|
||||||
assert_eq!(self.len(), frames.len());
|
assert_eq!(self.len(), frames.len());
|
||||||
let mut vm_map_options = VmMapOptions::new();
|
let mut vm_map_options = VmMapOptions::new();
|
||||||
vm_map_options.addr(Some(self.start_address()));
|
vm_map_options.addr(Some(self.start_address()));
|
||||||
vm_map_options.perm(vm_perm);
|
vm_map_options.perm(vm_perm);
|
||||||
vm_space.map(frames, &vm_map_options).expect("map failed");
|
vm_space.map(frames, &vm_map_options).expect("map failed");
|
||||||
self.vm_space = Some(vm_space)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmap(&mut self) {
|
pub fn unmap(&mut self, vm_space: &VmSpace) {
|
||||||
if self.is_mapped() {
|
vm_space
|
||||||
let vm_space = self.vm_space.take().unwrap();
|
.unmap(&(self.start_address()..self.end_address()))
|
||||||
vm_space
|
.expect("unmap failed");
|
||||||
.unmap(&(self.start_address()..self.end_address()))
|
|
||||||
.expect("unmap failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_mapped(&self) -> bool {
|
pub fn is_mapped(&self, vm_space: &VmSpace) -> bool {
|
||||||
if let None = self.vm_space {
|
todo!()
|
||||||
false
|
}
|
||||||
} else {
|
|
||||||
true
|
pub fn iter(&self) -> VmPageIter<'_> {
|
||||||
|
VmPageIter {
|
||||||
|
current: self.start_page,
|
||||||
|
page_range: self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,8 +99,27 @@ impl<'a> VmPageRange<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct VmPageIter<'a> {
|
||||||
|
current: VmPage,
|
||||||
|
page_range: &'a VmPageRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for VmPageIter<'a> {
|
||||||
|
type Item = VmPage;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let next_page = if self.current <= self.page_range.end_page {
|
||||||
|
Some(self.current)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.current = self.current.next_page();
|
||||||
|
next_page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A Virtual Page
|
/// A Virtual Page
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct VmPage {
|
pub struct VmPage {
|
||||||
/// Virtual Page Number
|
/// Virtual Page Number
|
||||||
vpn: usize,
|
vpn: usize,
|
||||||
@ -115,4 +135,25 @@ impl VmPage {
|
|||||||
const fn start_address(&self) -> Vaddr {
|
const fn start_address(&self) -> Vaddr {
|
||||||
self.vpn * PAGE_SIZE
|
self.vpn * PAGE_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn next_page(&self) -> VmPage {
|
||||||
|
VmPage { vpn: self.vpn + 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether current page is mapped
|
||||||
|
pub fn is_mapped(&self, vm_space: &VmSpace) -> bool {
|
||||||
|
vm_space.is_mapped(self.start_address())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_page(&self, vm_space: &VmSpace, vm_perm: VmPerm) -> Result<(), ElfError> {
|
||||||
|
let vm_alloc_option = VmAllocOptions::new(1);
|
||||||
|
let vm_frame = VmFrameVec::allocate(&vm_alloc_option)?;
|
||||||
|
|
||||||
|
let mut vm_map_options = VmMapOptions::new();
|
||||||
|
vm_map_options.addr(Some(self.start_address()));
|
||||||
|
vm_map_options.perm(vm_perm);
|
||||||
|
vm_space.map(vm_frame, &vm_map_options)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7
src/kxos-user/hello_c/Makefile
Normal file
7
src/kxos-user/hello_c/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.PHONY: build clean run
|
||||||
|
build: hello.c
|
||||||
|
@gcc -static hello.c -o hello
|
||||||
|
clean:
|
||||||
|
@rm hello
|
||||||
|
run: build
|
||||||
|
@./hello
|
3
src/kxos-user/hello_c/hello
Executable file
3
src/kxos-user/hello_c/hello
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:06a00e3155168337417bf707d6f2d47321f9ca2f6a52a73d3dceb6bd1b807e95
|
||||||
|
size 871896
|
6
src/kxos-user/hello_c/hello.c
Normal file
6
src/kxos-user/hello_c/hello.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("hello world!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user