Compare commits
61 Commits
10cf618834
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 86ca343cf0 | |||
| b680ffdd5b | |||
| 14799e0d58 | |||
| b7452a449b | |||
| ea6ec785a9 | |||
| 6d88cf4bf3 | |||
| aef0163017 | |||
| b0fda122e0 | |||
| 79af171384 | |||
| 5931642cc2 | |||
| 26a49162e6 | |||
| 3a06c18e10 | |||
| e46c20b572 | |||
| 0a07db360c | |||
| 1f2bd09d57 | |||
| 72b5801292 | |||
| 5ad1babd03 | |||
| d0abe3333d | |||
| 5658e27445 | |||
| c4fd252f86 | |||
| a6a2526502 | |||
| faeefe28ab | |||
| 1c89acddbf | |||
| f47be0cd67 | |||
| 68714fa0e5 | |||
| 267b893bf4 | |||
| d6b3af6e67 | |||
| 4be6f231e6 | |||
| 598bdb1410 | |||
| 285a80530d | |||
| 3b50715559 | |||
| bdb2e0ed50 | |||
| 01109948b2 | |||
| dc93f7db4c | |||
| b631fca0fd | |||
| e2b19c3e9a | |||
| 411423bf30 | |||
| 0183790af3 | |||
| 418c426e83 | |||
| eca9d3915b | |||
| d32988c56c | |||
| 02d4f43151 | |||
| 1645fdfb42 | |||
| dbb2d72bcb | |||
| 67187e457f | |||
| f778762545 | |||
| c39e0b206a | |||
| 562a729c25 | |||
| 5721daa049 | |||
| fe3b014e09 | |||
| 67ae16a756 | |||
| 5bc9ff4fd1 | |||
| 728e1f057c | |||
| b62052f619 | |||
| 2c41f1a77a | |||
| 7a51a56909 | |||
| cff399fdba | |||
| c476b08c03 | |||
| 32aaacb95f | |||
| 0b7b8d3f29 | |||
| cc4b9d4a9b |
@@ -11,12 +11,17 @@ include(Meta)
|
||||
include(Sysroot)
|
||||
include(BSP)
|
||||
include(Arch)
|
||||
include(Msg-Interface)
|
||||
include(Templates)
|
||||
|
||||
bsp_reset()
|
||||
sysroot_reset()
|
||||
|
||||
sysroot_set_base(
|
||||
PATH ${CMAKE_SOURCE_DIR}/base)
|
||||
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(sys)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(services)
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
include(System-Disk)
|
||||
include(QEMU)
|
||||
include(Bochs)
|
||||
|
||||
18
arch/x86_64/Bochs.cmake
Normal file
18
arch/x86_64/Bochs.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
find_program(BOCHS bochs)
|
||||
|
||||
if (NOT BOCHS)
|
||||
message(STATUS "Bochs: cannot find bochs")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET cdrom)
|
||||
message(STATUS "Bochs: CD-ROM image creation is unavailable. Cannot use bochs")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
message(STATUS "Bochs: Enable CD-ROM boot")
|
||||
add_custom_target(run-cdrom-bochs
|
||||
COMMAND ${BOCHS} -q -f ${CMAKE_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/bochsrc.bxrc
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
USES_TERMINAL
|
||||
DEPENDS cdrom)
|
||||
@@ -15,7 +15,7 @@ set(CMAKE_ASM_COMPILER ${ASM_COMPILER})
|
||||
|
||||
SET(CMAKE_C_FLAGS "-ffreestanding -nostdlib -z max-page-size=0x1000 -m64 -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -D_64BIT -DBYTE_ORDER=1234" CACHE STRING "" FORCE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-shared" CACHE STRING "" FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--unresolved-symbols=report-all,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
|
||||
|
||||
set(CMAKE_C_OUTPUT_EXTENSION .o)
|
||||
set(CMAKE_CXX_OUTPUT_EXTENSION .o)
|
||||
|
||||
@@ -1,24 +1,123 @@
|
||||
find_program(QEMU qemu-system-${TARGET_ARCH} REQUIRED)
|
||||
find_program(LLDB lldb REQUIRED)
|
||||
if (NOT QEMU)
|
||||
message(STATUS "QEMU: Cannot find qemu-system-${TARGET_ARCH}. Direct-kernel boot unavailable")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
find_program(LLDB lldb)
|
||||
find_program(GDB gdb)
|
||||
|
||||
set(patched_kernel ${CMAKE_CURRENT_BINARY_DIR}/kernel/${kernel_name}.elf32)
|
||||
set(generic_flags -m 1G)
|
||||
set(no_debug_flags)
|
||||
|
||||
if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
|
||||
message(STATUS "QEMU: Enabling KVM acceleration")
|
||||
set(no_debug_flags ${no_debug_flags} -enable-kvm)
|
||||
else ()
|
||||
message(STATUS "QEMU: Host system is not Linux. KVM acceleration unavailable")
|
||||
endif ()
|
||||
|
||||
add_custom_command(OUTPUT ${patched_kernel}
|
||||
DEPENDS ${kernel_name}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
$<TARGET_FILE:${kernel_name}>
|
||||
${patched_kernel}
|
||||
COMMAND ${BUILD_TOOLS_DIR}/e64patch ${patched_kernel}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Patching kernel elf64 image"
|
||||
)
|
||||
|
||||
message(STATUS "QEMU: Enable direct-kernel boot")
|
||||
add_custom_target(run-kernel
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel $<TARGET_FILE:${kernel_name}>
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
-m 1G -serial stdio
|
||||
${generic_flags} ${no_debug_flags}
|
||||
-serial stdio
|
||||
--append kernel.early-console=ttyS0
|
||||
USES_TERMINAL
|
||||
DEPENDS ${kernel_name} bsp)
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
add_custom_target(debug-kernel
|
||||
add_custom_target(run-kernel-monitor
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel $<TARGET_FILE:${kernel_name}>
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
-m 1G -s -S &
|
||||
${LLDB}
|
||||
-o "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-o "gdb-remote localhost:1234"
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags} ${no_debug_flags}
|
||||
-monitor stdio
|
||||
USES_TERMINAL
|
||||
DEPENDS ${kernel_name} bsp)
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
if (image_cdrom)
|
||||
message(STATUS "QEMU: Enable CD-ROM boot")
|
||||
add_custom_target(run-cdrom
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-cdrom ${image_cdrom}
|
||||
${generic_flags} ${no_debug_flags}
|
||||
-serial stdio
|
||||
USES_TERMINAL
|
||||
DEPENDS ${image_cdrom})
|
||||
endif ()
|
||||
|
||||
if (LLDB)
|
||||
message(STATUS "QEMU: Enable direct-kernel debug with LLDB")
|
||||
add_custom_target(debug-kernel
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${LLDB}
|
||||
-o "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-o "gdb-remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
if (image_cdrom)
|
||||
message(STATUS "QEMU: Enable CD-ROM debug with LLDB")
|
||||
add_custom_target(debug-cdrom
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-cdrom ${image_cdrom}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${LLDB}
|
||||
-o "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-o "target remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS cdrom)
|
||||
endif ()
|
||||
elseif (GDB)
|
||||
message(STATUS "QEMU: Enable direct-kernel debug with GDB")
|
||||
add_custom_target(debug-kernel
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${GDB} -tui
|
||||
-ex "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-ex "target remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
if (image_cdrom)
|
||||
message(STATUS "QEMU: Enable CD-ROM debug with GDB")
|
||||
add_custom_target(debug-cdrom
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-cdrom ${image_cdrom}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${GDB} -tui
|
||||
-ex "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-ex "target remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS ${image_cdrom})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
17
arch/x86_64/System-Disk.cmake
Normal file
17
arch/x86_64/System-Disk.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
find_program(GRUB_MKRESCUE grub-mkrescue)
|
||||
|
||||
if (GRUB_MKRESCUE)
|
||||
message(STATUS "GRUB: Found grub-mkrescue. Bootable CD-ROM image creation is enabled")
|
||||
set(image_cdrom ${CMAKE_CURRENT_BINARY_DIR}/rosetta-system.iso)
|
||||
|
||||
add_custom_target(cdrom
|
||||
DEPENDS sysroot
|
||||
COMMAND ${GRUB_MKRESCUE}
|
||||
-o ${image_cdrom}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sysroot
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Building GRUB CD-ROM image"
|
||||
)
|
||||
else ()
|
||||
message(STATUS "Cannot find grub-mkrescue. Bootable CD-ROM image creation is unavailable")
|
||||
endif ()
|
||||
53
arch/x86_64/bochsrc.bxrc
Normal file
53
arch/x86_64/bochsrc.bxrc
Normal file
@@ -0,0 +1,53 @@
|
||||
# configuration file generated by Bochs
|
||||
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true
|
||||
config_interface: textconfig
|
||||
display_library: sdl2
|
||||
memory: guest=128, host=128, block_size=128
|
||||
romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none, flash_data=none
|
||||
vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest.bin"
|
||||
boot: cdrom
|
||||
floppy_bootsig_check: disabled=0
|
||||
floppya: type=1_44
|
||||
# no floppyb
|
||||
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||
ata0-master: type=cdrom, path="rosetta-system.iso", status=inserted, model="Generic 1234", biosdetect=auto
|
||||
ata0-slave: type=none
|
||||
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||
ata1-master: type=none
|
||||
ata1-slave: type=none
|
||||
ata2: enabled=false
|
||||
ata3: enabled=false
|
||||
optromimage1: file=none
|
||||
optromimage2: file=none
|
||||
optromimage3: file=none
|
||||
optromimage4: file=none
|
||||
optramimage1: file=none
|
||||
optramimage2: file=none
|
||||
optramimage3: file=none
|
||||
optramimage4: file=none
|
||||
pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none
|
||||
vga: extension=vbe, update_freq=10, realtime=1, ddc=builtin, vbe_memsize=16
|
||||
cpu: count=1:1:1, ips=4000000, quantum=16, model=core2_penryn_t9600, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
|
||||
print_timestamps: enabled=0
|
||||
debugger_log: -
|
||||
magic_break: enabled=1 0x0
|
||||
port_e9_hack: enabled=false, all_rings=false
|
||||
iodebug: all_rings=0
|
||||
private_colormap: enabled=0
|
||||
clock: sync=none, time0=local, rtc_sync=0
|
||||
# no cmosimage
|
||||
log: -
|
||||
logprefix: %t%e%d
|
||||
debug: action=ignore
|
||||
info: action=report
|
||||
error: action=report
|
||||
panic: action=ask
|
||||
keyboard: type=mf, serial_delay=150, paste_delay=100000, user_shortcut=none
|
||||
mouse: type=ps2, enabled=false, toggle=ctrl+mbutton
|
||||
speaker: enabled=true, mode=system
|
||||
parport1: enabled=true, file=none
|
||||
parport2: enabled=false
|
||||
com1: enabled=true, mode=null
|
||||
com2: enabled=false
|
||||
com3: enabled=false
|
||||
com4: enabled=false
|
||||
11
base/boot/grub/grub.cfg
Normal file
11
base/boot/grub/grub.cfg
Normal file
@@ -0,0 +1,11 @@
|
||||
menuentry "Rosetta" {
|
||||
multiboot /boot/mango_kernel
|
||||
module /boot/rosetta-system.bsp
|
||||
boot
|
||||
}
|
||||
|
||||
menuentry "Rosetta (Serial Log)" {
|
||||
multiboot /boot/mango_kernel kernel.early-console=ttyS0
|
||||
module /boot/rosetta-system.bsp
|
||||
boot
|
||||
}
|
||||
75
cmake/Msg-Interface.cmake
Normal file
75
cmake/Msg-Interface.cmake
Normal file
@@ -0,0 +1,75 @@
|
||||
find_program(XPCG
|
||||
NAMES xpcg
|
||||
REQUIRED
|
||||
HINTS ${BUILD_TOOLS_DIR})
|
||||
message(STATUS "Found interface generator: ${XPCG}")
|
||||
|
||||
function(add_interface)
|
||||
set(options)
|
||||
set(one_value_args NAME PARENT_DIR PATH)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
message(STATUS "Building interface ${arg_NAME}")
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR})
|
||||
set(header_path ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR}/${arg_NAME}.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${header_path}
|
||||
COMMAND ${XPCG} ${arg_PATH}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR}
|
||||
COMMENT "Generating interface: ${arg_NAME}")
|
||||
|
||||
add_custom_target(ifgen-${arg_NAME} ALL
|
||||
DEPENDS ${header_path})
|
||||
|
||||
set_target_properties(ifgen-${arg_NAME} PROPERTIES
|
||||
iface_header_path ${header_path}
|
||||
iface_parent_dir ${arg_PARENT_DIR})
|
||||
|
||||
add_library(iflib-${arg_NAME} INTERFACE)
|
||||
set_target_properties(iflib-${arg_NAME} PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_dependencies(iflib-${arg_NAME} ifgen-${arg_NAME})
|
||||
add_library(interface::${arg_NAME} ALIAS iflib-${arg_NAME})
|
||||
endfunction(add_interface)
|
||||
|
||||
function(iface_get_header_path)
|
||||
set(options)
|
||||
set(one_value_args NAME OUT)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
get_property(
|
||||
value
|
||||
TARGET ifgen-${name}
|
||||
PROPERTY iface_header_path)
|
||||
|
||||
set(${arg_OUT} ${value} PARENT_SCOPE)
|
||||
endfunction(iface_get_header_path)
|
||||
|
||||
function(iface_get_parent_dir)
|
||||
set(options)
|
||||
set(one_value_args NAME OUT)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
get_property(
|
||||
value
|
||||
TARGET ifgen-${name}
|
||||
PROPERTY iface_parent_dir)
|
||||
|
||||
set(${arg_OUT} ${value} PARENT_SCOPE)
|
||||
endfunction(iface_get_parent_dir)
|
||||
@@ -9,6 +9,34 @@ function(sysroot_reset)
|
||||
COMMAND_ERROR_IS_FATAL ANY)
|
||||
endfunction(sysroot_reset)
|
||||
|
||||
function(sysroot_set_base)
|
||||
set(options)
|
||||
set(one_value_args PATH)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(sysroot_target_name _sysroot-base)
|
||||
|
||||
get_property(sysroot_targets GLOBAL PROPERTY sysroot_target_list)
|
||||
list(LENGTH sysroot_targets nr_sysroot_targets)
|
||||
if (${nr_sysroot_targets} GREATER 0)
|
||||
math(EXPR serialiser_index "${nr_sysroot_targets}-1")
|
||||
list(GET sysroot_targets ${serialiser_index} serialiser)
|
||||
endif ()
|
||||
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
set-base ${sysroot_manifest} ${arg_PATH}
|
||||
COMMENT "Preparing sysroot base"
|
||||
DEPENDS ${serialiser})
|
||||
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_set_base)
|
||||
|
||||
function(sysroot_add_library)
|
||||
set(options)
|
||||
set(one_value_args NAME HEADER_DIR LIB_DIR)
|
||||
@@ -93,7 +121,6 @@ function(sysroot_add_object_library)
|
||||
endif ()
|
||||
|
||||
get_property(tmp TARGET ${target_name} PROPERTY SUFFIX)
|
||||
message(STATUS ${tmp})
|
||||
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_add_object_library)
|
||||
@@ -159,6 +186,37 @@ function(sysroot_add_file)
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_add_file)
|
||||
|
||||
function(sysroot_add_interface)
|
||||
set(options)
|
||||
set(one_value_args NAME DEST_DIR)
|
||||
set(multi_value_args DEPENDS)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
iface_get_header_path(NAME ${arg_NAME} OUT src_path)
|
||||
iface_get_parent_dir(NAME ${arg_NAME} OUT parent_dir)
|
||||
set(dest_dir ${arg_DEST_DIR}/${parent_dir})
|
||||
set(sysroot_target_name _sysroot-iface-${arg_NAME})
|
||||
|
||||
get_property(sysroot_targets GLOBAL PROPERTY sysroot_target_list)
|
||||
list(LENGTH sysroot_targets nr_sysroot_targets)
|
||||
if (${nr_sysroot_targets} GREATER 0)
|
||||
math(EXPR serialiser_index "${nr_sysroot_targets}-1")
|
||||
list(GET sysroot_targets ${serialiser_index} serialiser)
|
||||
endif ()
|
||||
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-binary ${sysroot_manifest} ${arg_NAME}
|
||||
${dest_dir} ${src_path}
|
||||
COMMENT "Preparing sysroot component: ${arg_ID}"
|
||||
DEPENDS ifgen-${arg_NAME} ${serialiser} ${arg_DEPENDS})
|
||||
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_add_interface)
|
||||
|
||||
function(sysroot_finalise)
|
||||
get_property(sysroot_targets GLOBAL PROPERTY sysroot_target_list)
|
||||
|
||||
@@ -63,6 +63,9 @@ function(rosetta_add_library)
|
||||
add_library(${static_lib_name} STATIC
|
||||
${arg_SOURCES}
|
||||
${arg_HEADERS})
|
||||
set_target_properties(${static_lib_name} PROPERTIES OUTPUT_NAME "${lib_name}")
|
||||
target_compile_definitions(${static_lib_name} PRIVATE
|
||||
BUILD_STATIC=1)
|
||||
set(targets ${targets} ${static_lib_name})
|
||||
|
||||
if (arg_PUBLIC_INCLUDE_DIRS)
|
||||
@@ -85,12 +88,17 @@ function(rosetta_add_library)
|
||||
PATH ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
target_compile_definitions(${shared_lib_name} PRIVATE
|
||||
BUILD_SHARED=1)
|
||||
set_target_properties(${shared_lib_name} PROPERTIES
|
||||
SOVERSION 1)
|
||||
target_link_options(${shared_lib_name} PRIVATE -Wl,--soname,${soname})
|
||||
endif()
|
||||
|
||||
target_include_directories(${targets} PUBLIC ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
foreach (target ${targets})
|
||||
target_include_directories(${target} PUBLIC ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
endforeach (target)
|
||||
|
||||
set_target_properties(${targets} PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
src_header_dir ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
|
||||
@@ -8,8 +8,9 @@ if [ ! -n "$target_arch" ]; then
|
||||
exit -1
|
||||
fi
|
||||
|
||||
build_type=Debug
|
||||
source_dir=$(realpath $(dirname "$0"))
|
||||
native_build_dir=$source_dir/build-native
|
||||
toolchain_build_dir=$source_dir/build/toolchain
|
||||
target_build_dir=$source_dir/build
|
||||
sysroot_dir=$target_build_dir/sysroot
|
||||
|
||||
@@ -18,30 +19,47 @@ if [ ! -d "$source_dir/arch/$target_arch" ]; then
|
||||
exit -1
|
||||
fi
|
||||
|
||||
rm -rf $native_build_dir $target_build_dir
|
||||
mkdir -p $native_build_dir $target_build_dir
|
||||
rm -rf $toolchain_build_dir $target_build_dir
|
||||
mkdir -p $toolchain_build_dir $target_build_dir
|
||||
|
||||
pushd $native_build_dir > /dev/null
|
||||
pushd $toolchain_build_dir > /dev/null
|
||||
|
||||
cmake \
|
||||
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY="$native_build_dir/bin" \
|
||||
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$native_build_dir/lib" \
|
||||
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$native_build_dir/lib" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
$source_dir/kernel/tools
|
||||
-DCMAKE_BUILD_TYPE=$build_type \
|
||||
$source_dir/toolchain
|
||||
|
||||
case $CMAKE_GENERATOR in
|
||||
Ninja)
|
||||
ninja
|
||||
;;
|
||||
"Unix Makefiles")
|
||||
make
|
||||
;;
|
||||
|
||||
*)
|
||||
make
|
||||
;;
|
||||
esac
|
||||
|
||||
popd
|
||||
|
||||
pushd $target_build_dir > /dev/null
|
||||
|
||||
cmake \
|
||||
-DBUILD_TOOLS_DIR=$native_build_dir/bin \
|
||||
-DBUILD_TOOLS_DIR=$toolchain_build_dir/bin \
|
||||
-DCMAKE_INSTALL_PREFIX=$sysroot_dir \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DTARGET_ARCH=$target_arch \
|
||||
-DCMAKE_BUILD_TYPE=$build_type \
|
||||
$source_dir \
|
||||
-DCMAKE_MODULE_PATH=$source_dir/arch/$target_arch \
|
||||
-DCMAKE_SYSTEM_NAME=Rosetta \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$source_dir/arch/$target_arch/Platform/Rosetta.cmake
|
||||
|
||||
python3 \
|
||||
$source_dir/util/merge-compiledb.py \
|
||||
compile_commands.json \
|
||||
toolchain/compile_commands.json
|
||||
|
||||
popd > /dev/null
|
||||
|
||||
7
interface/CMakeLists.txt
Normal file
7
interface/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
file(GLOB if_files *.xpc)
|
||||
|
||||
foreach (file ${if_files})
|
||||
get_filename_component(name ${file} NAME_WLE)
|
||||
add_interface(NAME ${name} PATH ${file} PARENT_DIR rosetta)
|
||||
sysroot_add_interface(NAME ${name} DEST_DIR /usr/include)
|
||||
endforeach (file)
|
||||
9
interface/fs.xpc
Normal file
9
interface/fs.xpc
Normal file
@@ -0,0 +1,9 @@
|
||||
interface fs 6400;
|
||||
|
||||
func open[0](path: string, flags: int) -> (err: int);
|
||||
func close[1]() -> (err: int);
|
||||
|
||||
func read[2](count: size) -> (err: int, nr_read: size, data: buffer);
|
||||
func write[3](data: buffer) -> (err: int, nr_written: size);
|
||||
|
||||
func map[4](prot: int, flags: int) -> (vmo: handle);
|
||||
2
kernel
2
kernel
Submodule kernel updated: 9a90662eaa...de520cdd2d
@@ -1,36 +1,30 @@
|
||||
set(source_dirs string stdio)
|
||||
foreach (dir ${source_dirs})
|
||||
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
|
||||
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
|
||||
|
||||
set(sources ${sources} ${dir_sources})
|
||||
set(headers ${headers} ${dir_headers})
|
||||
endforeach (dir)
|
||||
|
||||
file(GLOB runtime_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/runtime/${CMAKE_SYSTEM_PROCESSOR}/*.s)
|
||||
set_property(SOURCE ${runtime_sources} PROPERTY LANGUAGE C)
|
||||
set(source_dirs core malloc io)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
add_subdirectory(runtime)
|
||||
|
||||
foreach (dir ${source_dirs})
|
||||
add_subdirectory(${dir})
|
||||
set(sources ${sources} ${component_sources})
|
||||
set(headers ${headers} ${component_headers})
|
||||
set(public_include_dirs ${public_include_dirs} ${component_public_include_dirs})
|
||||
endforeach (dir)
|
||||
|
||||
rosetta_add_library(SHARED
|
||||
NAME libc
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
rosetta_add_object_library(
|
||||
NAME libc-rt STATIC
|
||||
SOURCES ${runtime_sources})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libc
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
sysroot_add_object_library(
|
||||
NAME libc-rt
|
||||
LIB_DIR /usr/lib)
|
||||
bsp_add_library(
|
||||
NAME libc
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libc libmango libxpc-static interface::fs)
|
||||
target_compile_definitions(libc PRIVATE ENABLE_GLOBAL_HEAP=1)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
set(source_dirs string stdio unistd)
|
||||
set(source_dirs stdio string errno)
|
||||
|
||||
foreach (dir ${source_dirs})
|
||||
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
|
||||
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
|
||||
@@ -7,13 +8,18 @@ foreach (dir ${source_dirs})
|
||||
set(headers ${headers} ${dir_headers})
|
||||
endforeach (dir)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
set(component_sources ${sources} PARENT_SCOPE)
|
||||
set(component_headers ${headers} PARENT_SCOPE)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME ulibc STATIC
|
||||
rosetta_add_library(STATIC
|
||||
NAME libc-core
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
target_link_libraries(ulibc libmango)
|
||||
sysroot_add_library(
|
||||
NAME libc-core
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libc-core libmango)
|
||||
54
lib/libc/core/errno/errno.c
Normal file
54
lib/libc/core/errno/errno.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <errno.h>
|
||||
#include <mango/status.h>
|
||||
|
||||
#if defined(BUILD_STATIC)
|
||||
int __set_errno(int err)
|
||||
{
|
||||
return -err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BUILD_SHARED)
|
||||
int __set_errno(int err)
|
||||
{
|
||||
/* TODO */
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int __errno_from_kern_status(unsigned int err)
|
||||
{
|
||||
switch (err) {
|
||||
case KERN_OK:
|
||||
return SUCCESS;
|
||||
case KERN_UNIMPLEMENTED:
|
||||
return ENOSYS;
|
||||
case KERN_NAME_EXISTS:
|
||||
return EEXIST;
|
||||
case KERN_INVALID_ARGUMENT:
|
||||
return EINVAL;
|
||||
case KERN_UNSUPPORTED:
|
||||
return ENOTSUP;
|
||||
case KERN_NO_MEMORY:
|
||||
return ENOMEM;
|
||||
case KERN_NO_ENTRY:
|
||||
return ENOENT;
|
||||
case KERN_WOULD_BLOCK:
|
||||
return EWOULDBLOCK;
|
||||
case KERN_NO_DEVICE:
|
||||
return ENODEV;
|
||||
case KERN_DEVICE_STUCK:
|
||||
case KERN_IO_ERROR:
|
||||
return EIO;
|
||||
case KERN_FATAL_ERROR:
|
||||
return ENXIO;
|
||||
case KERN_BAD_STATE:
|
||||
return EPERM;
|
||||
case KERN_MEMORY_FAULT:
|
||||
return EFAULT;
|
||||
case KERN_ACCESS_DENIED:
|
||||
return EACCES;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
342
lib/libc/core/string/strerror.c
Normal file
342
lib/libc/core/string/strerror.c
Normal file
@@ -0,0 +1,342 @@
|
||||
#include <errno.h>
|
||||
|
||||
const char *strerror(int errnum)
|
||||
{
|
||||
switch (errnum) {
|
||||
case SUCCESS:
|
||||
return "Success";
|
||||
case EPERM:
|
||||
return "Operation not permitted";
|
||||
case ENOENT:
|
||||
return "No such file or directory";
|
||||
case ESRCH:
|
||||
return "No such process";
|
||||
case EINTR:
|
||||
return "Interrupted system call";
|
||||
case EIO:
|
||||
return "Input/output error";
|
||||
case ENXIO:
|
||||
return "Device not configured";
|
||||
case E2BIG:
|
||||
return "Argument list too long";
|
||||
case ENOEXEC:
|
||||
return "Exec format error";
|
||||
case EBADF:
|
||||
return "Bad file descriptor";
|
||||
case ECHILD:
|
||||
return "No child processes";
|
||||
case EDEADLK:
|
||||
return "Resource deadlock avoided";
|
||||
case ENOMEM:
|
||||
return "Cannot allocate memory";
|
||||
case EACCES:
|
||||
return "Permission denied";
|
||||
case EFAULT:
|
||||
return "Bad address";
|
||||
case ENOTBLK:
|
||||
return "Block device required";
|
||||
case EBUSY:
|
||||
return "Resource busy";
|
||||
case EEXIST:
|
||||
return "File exists";
|
||||
case EXDEV:
|
||||
return "Cross-device link";
|
||||
case ENODEV:
|
||||
return "Operation not supported by device";
|
||||
case ENOTDIR:
|
||||
return "Not a directory";
|
||||
case EISDIR:
|
||||
return "Is a directory";
|
||||
case EINVAL:
|
||||
return "Invalid argument";
|
||||
case ENFILE:
|
||||
return "Too many open files in system";
|
||||
case EMFILE:
|
||||
return "Too many open files";
|
||||
case ENOTTY:
|
||||
return "Inappropriate ioctl for device";
|
||||
case ETXTBSY:
|
||||
return "Text file busy";
|
||||
case EFBIG:
|
||||
return "File too large";
|
||||
case ENOSPC:
|
||||
return "No space left on device";
|
||||
case ESPIPE:
|
||||
return "Illegal seek";
|
||||
case EROFS:
|
||||
return "Read-only file system";
|
||||
case EMLINK:
|
||||
return "Too many links";
|
||||
case EPIPE:
|
||||
return "Broken pipe";
|
||||
case EDOM:
|
||||
return "Numerical argument out of domain";
|
||||
case ERANGE:
|
||||
return "Result too large";
|
||||
case EAGAIN:
|
||||
return "Resource temporarily unavailable";
|
||||
case EINPROGRESS:
|
||||
return "Operation now in progress";
|
||||
case EALREADY:
|
||||
return "Operation already in progress";
|
||||
case ENOTSOCK:
|
||||
return "Socket operation on non-socket";
|
||||
case EDESTADDRREQ:
|
||||
return "Destination address required";
|
||||
case EMSGSIZE:
|
||||
return "Message too long";
|
||||
case EPROTOTYPE:
|
||||
return "Protocol wrong type for socket";
|
||||
case ENOPROTOOPT:
|
||||
return "Protocol not available";
|
||||
case EPROTONOSUPPORT:
|
||||
return "Protocol not supported";
|
||||
case ESOCKTNOSUPPORT:
|
||||
return "Socket type not supported";
|
||||
case ENOTSUP:
|
||||
return "Operation not supported";
|
||||
case EPFNOSUPPORT:
|
||||
return "Protocol family not supported";
|
||||
case EAFNOSUPPORT:
|
||||
return "Address family not supported by protocol family";
|
||||
case EADDRINUSE:
|
||||
return "Address already in use";
|
||||
case EADDRNOTAVAIL:
|
||||
return "Can't assign requested address";
|
||||
case ENETDOWN:
|
||||
return "Network is down";
|
||||
case ENETUNREACH:
|
||||
return "Network is unreachable";
|
||||
case ENETRESET:
|
||||
return "Network dropped connection on reset";
|
||||
case ECONNABORTED:
|
||||
return "Software caused connection abort";
|
||||
case ECONNRESET:
|
||||
return "Connection reset by peer";
|
||||
case ENOBUFS:
|
||||
return "No buffer space available";
|
||||
case EISCONN:
|
||||
return "Socket is already connected";
|
||||
case ENOTCONN:
|
||||
return "Socket is not connected";
|
||||
case ESHUTDOWN:
|
||||
return "Can't send after socket shutdown";
|
||||
case ETOOMANYREFS:
|
||||
return "Too many references: can't splice";
|
||||
case ETIMEDOUT:
|
||||
return "Operation timed out";
|
||||
case ECONNREFUSED:
|
||||
return "Connection refused";
|
||||
case ELOOP:
|
||||
return "Too many levels of symbolic links";
|
||||
case ENAMETOOLONG:
|
||||
return "File name too long";
|
||||
case EHOSTDOWN:
|
||||
return "Host is down";
|
||||
case EHOSTUNREACH:
|
||||
return "No route to host";
|
||||
case ENOTEMPTY:
|
||||
return "Directory not empty";
|
||||
case EPROCLIM:
|
||||
return "Too many processes";
|
||||
case EUSERS:
|
||||
return "Too many users";
|
||||
case EDQUOT:
|
||||
return "Disc quota exceeded";
|
||||
case ESTALE:
|
||||
return "Stale NFS file handle";
|
||||
case EREMOTE:
|
||||
return "Too many levels of remote in path";
|
||||
case EBADRPC:
|
||||
return "RPC struct is bad";
|
||||
case ERPCMISMATCH:
|
||||
return "RPC version wrong";
|
||||
case EPROGUNAVAIL:
|
||||
return "RPC prog. not avail";
|
||||
case EPROGMISMATCH:
|
||||
return "Program version wrong";
|
||||
case EPROCUNAVAIL:
|
||||
return "Bad procedure for program";
|
||||
case ENOLCK:
|
||||
return "No locks available";
|
||||
case ENOSYS:
|
||||
return "Function not implemented";
|
||||
case EFTYPE:
|
||||
return "Inappropriate file type or format";
|
||||
case EAUTH:
|
||||
return "Authentication error";
|
||||
case ENEEDAUTH:
|
||||
return "Need authenticator";
|
||||
case EPWROFF:
|
||||
return "Device power is off";
|
||||
case EDEVERR:
|
||||
return "Device error";
|
||||
case EOVERFLOW:
|
||||
return "Value too large to be stored in data type";
|
||||
case EBADEXEC:
|
||||
return "Bad executable (or shared library)";
|
||||
case EBADARCH:
|
||||
return "Bad CPU type in executable";
|
||||
case ESHLIBVERS:
|
||||
return "Shared library version mismatch";
|
||||
case EBADMACHO:
|
||||
return "Malformed Mach-o file";
|
||||
case ECANCELED:
|
||||
return "Operation canceled";
|
||||
case EIDRM:
|
||||
return "Identifier removed";
|
||||
case ENOMSG:
|
||||
return "No message of desired type";
|
||||
case EILSEQ:
|
||||
return "Illegal byte sequence";
|
||||
case ENOATTR:
|
||||
return "Attribute not found";
|
||||
case EBADMSG:
|
||||
return "Bad message";
|
||||
case EMULTIHOP:
|
||||
return "EMULTIHOP (Reserved)";
|
||||
case ENODATA:
|
||||
return "No message available on STREAM";
|
||||
case ENOLINK:
|
||||
return "ENOLINK (Reserved)";
|
||||
case ENOSR:
|
||||
return "No STREAM resources";
|
||||
case ENOSTR:
|
||||
return "Not a STREAM";
|
||||
case EPROTO:
|
||||
return "Protocol error";
|
||||
case ETIME:
|
||||
return "STREAM ioctl timeout";
|
||||
case EOPNOTSUPP:
|
||||
return "Operation not supported on socket";
|
||||
case ENOPOLICY:
|
||||
return "Policy not found";
|
||||
case ENOTRECOVERABLE:
|
||||
return "State not recoverable";
|
||||
case EOWNERDEAD:
|
||||
return "Previous owner died";
|
||||
case EQFULL:
|
||||
return "Interface output queue is full";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
#define ENUM_STR(x) \
|
||||
case x: \
|
||||
return #x
|
||||
|
||||
const char *strerror_code(int errnum)
|
||||
{
|
||||
switch (errnum) {
|
||||
ENUM_STR(SUCCESS);
|
||||
ENUM_STR(EPERM);
|
||||
ENUM_STR(ENOENT);
|
||||
ENUM_STR(ESRCH);
|
||||
ENUM_STR(EINTR);
|
||||
ENUM_STR(EIO);
|
||||
ENUM_STR(ENXIO);
|
||||
ENUM_STR(E2BIG);
|
||||
ENUM_STR(ENOEXEC);
|
||||
ENUM_STR(EBADF);
|
||||
ENUM_STR(ECHILD);
|
||||
ENUM_STR(EDEADLK);
|
||||
ENUM_STR(ENOMEM);
|
||||
ENUM_STR(EACCES);
|
||||
ENUM_STR(EFAULT);
|
||||
ENUM_STR(ENOTBLK);
|
||||
ENUM_STR(EBUSY);
|
||||
ENUM_STR(EEXIST);
|
||||
ENUM_STR(EXDEV);
|
||||
ENUM_STR(ENODEV);
|
||||
ENUM_STR(ENOTDIR);
|
||||
ENUM_STR(EISDIR);
|
||||
ENUM_STR(EINVAL);
|
||||
ENUM_STR(ENFILE);
|
||||
ENUM_STR(EMFILE);
|
||||
ENUM_STR(ENOTTY);
|
||||
ENUM_STR(ETXTBSY);
|
||||
ENUM_STR(EFBIG);
|
||||
ENUM_STR(ENOSPC);
|
||||
ENUM_STR(ESPIPE);
|
||||
ENUM_STR(EROFS);
|
||||
ENUM_STR(EMLINK);
|
||||
ENUM_STR(EPIPE);
|
||||
ENUM_STR(EDOM);
|
||||
ENUM_STR(ERANGE);
|
||||
ENUM_STR(EAGAIN);
|
||||
ENUM_STR(EINPROGRESS);
|
||||
ENUM_STR(EALREADY);
|
||||
ENUM_STR(ENOTSOCK);
|
||||
ENUM_STR(EDESTADDRREQ);
|
||||
ENUM_STR(EMSGSIZE);
|
||||
ENUM_STR(EPROTOTYPE);
|
||||
ENUM_STR(ENOPROTOOPT);
|
||||
ENUM_STR(EPROTONOSUPPORT);
|
||||
ENUM_STR(ESOCKTNOSUPPORT);
|
||||
ENUM_STR(ENOTSUP);
|
||||
ENUM_STR(EPFNOSUPPORT);
|
||||
ENUM_STR(EAFNOSUPPORT);
|
||||
ENUM_STR(EADDRINUSE);
|
||||
ENUM_STR(EADDRNOTAVAIL);
|
||||
ENUM_STR(ENETDOWN);
|
||||
ENUM_STR(ENETUNREACH);
|
||||
ENUM_STR(ENETRESET);
|
||||
ENUM_STR(ECONNABORTED);
|
||||
ENUM_STR(ECONNRESET);
|
||||
ENUM_STR(ENOBUFS);
|
||||
ENUM_STR(EISCONN);
|
||||
ENUM_STR(ENOTCONN);
|
||||
ENUM_STR(ESHUTDOWN);
|
||||
ENUM_STR(ETOOMANYREFS);
|
||||
ENUM_STR(ETIMEDOUT);
|
||||
ENUM_STR(ECONNREFUSED);
|
||||
ENUM_STR(ELOOP);
|
||||
ENUM_STR(ENAMETOOLONG);
|
||||
ENUM_STR(EHOSTDOWN);
|
||||
ENUM_STR(EHOSTUNREACH);
|
||||
ENUM_STR(ENOTEMPTY);
|
||||
ENUM_STR(EPROCLIM);
|
||||
ENUM_STR(EUSERS);
|
||||
ENUM_STR(EDQUOT);
|
||||
ENUM_STR(ESTALE);
|
||||
ENUM_STR(EREMOTE);
|
||||
ENUM_STR(EBADRPC);
|
||||
ENUM_STR(ERPCMISMATCH);
|
||||
ENUM_STR(EPROGUNAVAIL);
|
||||
ENUM_STR(EPROGMISMATCH);
|
||||
ENUM_STR(EPROCUNAVAIL);
|
||||
ENUM_STR(ENOLCK);
|
||||
ENUM_STR(ENOSYS);
|
||||
ENUM_STR(EFTYPE);
|
||||
ENUM_STR(EAUTH);
|
||||
ENUM_STR(ENEEDAUTH);
|
||||
ENUM_STR(EPWROFF);
|
||||
ENUM_STR(EDEVERR);
|
||||
ENUM_STR(EOVERFLOW);
|
||||
ENUM_STR(EBADEXEC);
|
||||
ENUM_STR(EBADARCH);
|
||||
ENUM_STR(ESHLIBVERS);
|
||||
ENUM_STR(EBADMACHO);
|
||||
ENUM_STR(ECANCELED);
|
||||
ENUM_STR(EIDRM);
|
||||
ENUM_STR(ENOMSG);
|
||||
ENUM_STR(EILSEQ);
|
||||
ENUM_STR(ENOATTR);
|
||||
ENUM_STR(EBADMSG);
|
||||
ENUM_STR(EMULTIHOP);
|
||||
ENUM_STR(ENODATA);
|
||||
ENUM_STR(ENOLINK);
|
||||
ENUM_STR(ENOSR);
|
||||
ENUM_STR(ENOSTR);
|
||||
ENUM_STR(EPROTO);
|
||||
ENUM_STR(ETIME);
|
||||
ENUM_STR(EOPNOTSUPP);
|
||||
ENUM_STR(ENOPOLICY);
|
||||
ENUM_STR(ENOTRECOVERABLE);
|
||||
ENUM_STR(EOWNERDEAD);
|
||||
ENUM_STR(EQFULL);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
145
lib/libc/include/errno.h
Normal file
145
lib/libc/include/errno.h
Normal file
@@ -0,0 +1,145 @@
|
||||
#ifndef ERRNO_H_
|
||||
#define ERRNO_H_
|
||||
|
||||
#define SUCCESS 0 /* Success */
|
||||
#define EPERM 1 /* Operation not permitted */
|
||||
#define ENOENT 2 /* No such file or directory */
|
||||
#define ESRCH 3 /* No such process */
|
||||
#define EINTR 4 /* Interrupted system call */
|
||||
#define EIO 5 /* Input/output error */
|
||||
#define ENXIO 6 /* Device not configured */
|
||||
#define E2BIG 7 /* Argument list too long */
|
||||
#define ENOEXEC 8 /* Exec format error */
|
||||
#define EBADF 9 /* Bad file descriptor */
|
||||
#define ECHILD 10 /* No child processes */
|
||||
#define EDEADLK 11 /* Resource deadlock avoided */
|
||||
#define ENOMEM 12 /* Cannot allocate memory */
|
||||
#define EACCES 13 /* Permission denied */
|
||||
#define EFAULT 14 /* Bad address */
|
||||
#define ENOTBLK 15 /* Block device required */
|
||||
#define EBUSY 16 /* Device / Resource busy */
|
||||
#define EEXIST 17 /* File exists */
|
||||
#define EXDEV 18 /* Cross-device link */
|
||||
#define ENODEV 19 /* Operation not supported by device */
|
||||
#define ENOTDIR 20 /* Not a directory */
|
||||
#define EISDIR 21 /* Is a directory */
|
||||
#define EINVAL 22 /* Invalid argument */
|
||||
#define ENFILE 23 /* Too many open files in system */
|
||||
#define EMFILE 24 /* Too many open files */
|
||||
#define ENOTTY 25 /* Inappropriate ioctl for device */
|
||||
#define ETXTBSY 26 /* Text file busy */
|
||||
#define EFBIG 27 /* File too large */
|
||||
#define ENOSPC 28 /* No space left on device */
|
||||
#define ESPIPE 29 /* Illegal seek */
|
||||
#define EROFS 30 /* Read-only file system */
|
||||
#define EMLINK 31 /* Too many links */
|
||||
#define EPIPE 32 /* Broken pipe */
|
||||
|
||||
/* math software */
|
||||
#define EDOM 33 /* Numerical argument out of domain */
|
||||
#define ERANGE 34 /* Result too large */
|
||||
|
||||
/* non-blocking and interrupt i/o */
|
||||
#define EAGAIN 35 /* Resource temporarily unavailable */
|
||||
#define EWOULDBLOCK EAGAIN /* Operation would block */
|
||||
#define EINPROGRESS 36 /* Operation now in progress */
|
||||
#define EALREADY 37 /* Operation already in progress */
|
||||
|
||||
/* ipc/network software -- argument errors */
|
||||
#define ENOTSOCK 38 /* Socket operation on non-socket */
|
||||
#define EDESTADDRREQ 39 /* Destination address required */
|
||||
#define EMSGSIZE 40 /* Message too long */
|
||||
#define EPROTOTYPE 41 /* Protocol wrong type for socket */
|
||||
#define ENOPROTOOPT 42 /* Protocol not available */
|
||||
#define EPROTONOSUPPORT 43 /* Protocol not supported */
|
||||
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
|
||||
#define ENOTSUP 45 /* Operation not supported */
|
||||
#define EPFNOSUPPORT 46 /* Protocol family not supported */
|
||||
#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */
|
||||
#define EADDRINUSE 48 /* Address already in use */
|
||||
#define EADDRNOTAVAIL 49 /* Can't assign requested address */
|
||||
|
||||
/* ipc/network software -- operational errors */
|
||||
#define ENETDOWN 50 /* Network is down */
|
||||
#define ENETUNREACH 51 /* Network is unreachable */
|
||||
#define ENETRESET 52 /* Network dropped connection on reset */
|
||||
#define ECONNABORTED 53 /* Software caused connection abort */
|
||||
#define ECONNRESET 54 /* Connection reset by peer */
|
||||
#define ENOBUFS 55 /* No buffer space available */
|
||||
#define EISCONN 56 /* Socket is already connected */
|
||||
#define ENOTCONN 57 /* Socket is not connected */
|
||||
#define ESHUTDOWN 58 /* Can't send after socket shutdown */
|
||||
#define ETOOMANYREFS 59 /* Too many references: can't splice */
|
||||
#define ETIMEDOUT 60 /* Operation timed out */
|
||||
#define ECONNREFUSED 61 /* Connection refused */
|
||||
|
||||
#define ELOOP 62 /* Too many levels of symbolic links */
|
||||
#define ENAMETOOLONG 63 /* File name too long */
|
||||
|
||||
#define EHOSTDOWN 64 /* Host is down */
|
||||
#define EHOSTUNREACH 65 /* No route to host */
|
||||
#define ENOTEMPTY 66 /* Directory not empty */
|
||||
|
||||
/* quotas & mush */
|
||||
#define EPROCLIM 67 /* Too many processes */
|
||||
#define EUSERS 68 /* Too many users */
|
||||
#define EDQUOT 69 /* Disc quota exceeded */
|
||||
|
||||
/* Network File System */
|
||||
#define ESTALE 70 /* Stale NFS file handle */
|
||||
#define EREMOTE 71 /* Too many levels of remote in path */
|
||||
#define EBADRPC 72 /* RPC struct is bad */
|
||||
#define ERPCMISMATCH 73 /* RPC version wrong */
|
||||
#define EPROGUNAVAIL 74 /* RPC prog. not avail */
|
||||
#define EPROGMISMATCH 75 /* Program version wrong */
|
||||
#define EPROCUNAVAIL 76 /* Bad procedure for program */
|
||||
|
||||
#define ENOLCK 77 /* No locks available */
|
||||
#define ENOSYS 78 /* Function not implemented */
|
||||
|
||||
#define EFTYPE 79 /* Inappropriate file type or format */
|
||||
#define EAUTH 80 /* Authentication error */
|
||||
#define ENEEDAUTH 81 /* Need authenticator */
|
||||
|
||||
/* Intelligent device errors */
|
||||
#define EPWROFF 82 /* Device power is off */
|
||||
#define EDEVERR 83 /* Device error, e.g. paper out */
|
||||
|
||||
#define EOVERFLOW 84 /* Value too large to be stored in data type */
|
||||
|
||||
/* Program loading errors */
|
||||
#define EBADEXEC 85 /* Bad executable */
|
||||
#define EBADARCH 86 /* Bad CPU type in executable */
|
||||
#define ESHLIBVERS 87 /* Shared library version mismatch */
|
||||
#define EBADMACHO 88 /* Malformed Macho file */
|
||||
|
||||
#define ECANCELED 89 /* Operation canceled */
|
||||
|
||||
#define EIDRM 90 /* Identifier removed */
|
||||
#define ENOMSG 91 /* No message of desired type */
|
||||
#define EILSEQ 92 /* Illegal byte sequence */
|
||||
#define ENOATTR 93 /* Attribute not found */
|
||||
|
||||
#define EBADMSG 94 /* Bad message */
|
||||
#define EMULTIHOP 95 /* Reserved */
|
||||
#define ENODATA 96 /* No message available on STREAM */
|
||||
#define ENOLINK 97 /* Reserved */
|
||||
#define ENOSR 98 /* No STREAM resources */
|
||||
#define ENOSTR 99 /* Not a STREAM */
|
||||
#define EPROTO 100 /* Protocol error */
|
||||
#define ETIME 101 /* STREAM ioctl timeout */
|
||||
|
||||
#define EOPNOTSUPP 102 /* Operation not supported on socket */
|
||||
|
||||
#define ENOPOLICY 103 /* No such policy registered */
|
||||
|
||||
#define ENOTRECOVERABLE 104 /* State not recoverable */
|
||||
#define EOWNERDEAD 105 /* Previous owner died */
|
||||
|
||||
#define EQFULL 106 /* Interface output queue is full */
|
||||
#define ELAST 106 /* Must be equal largest errno */
|
||||
|
||||
extern int __set_errno(int err);
|
||||
extern int __errno_from_kern_status(unsigned int err);
|
||||
|
||||
#endif
|
||||
13
lib/libc/include/stdlib.h
Normal file
13
lib/libc/include/stdlib.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef STDLIB_H_
|
||||
#define STDLIB_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern void abort(void);
|
||||
|
||||
extern void *malloc(size_t count);
|
||||
extern void *calloc(size_t count, size_t size);
|
||||
extern void *realloc(void *p, size_t count);
|
||||
extern void free(void *p);
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern const char *strerror(int errnum);
|
||||
extern const char *strerror_code(int errnum);
|
||||
|
||||
extern size_t strlen(const char *s);
|
||||
|
||||
extern int strcmp(const char *s1, const char *s2);
|
||||
@@ -11,4 +14,6 @@ extern int strncmp(const char *s1, const char *s2, unsigned long n);
|
||||
extern void *memset(void *str, int c, size_t n);
|
||||
extern void *memcpy(void *dst, const void *src, size_t len);
|
||||
|
||||
extern char *strdup(char *s);
|
||||
|
||||
#endif
|
||||
|
||||
12
lib/libc/include/unistd.h
Normal file
12
lib/libc/include/unistd.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef UNISTD_H_
|
||||
#define UNISTD_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern int open(const char *path, int flags);
|
||||
extern int close(int fd);
|
||||
|
||||
extern int read(int fd, void *buf, size_t count);
|
||||
extern int write(int fd, const void *buf, size_t count);
|
||||
|
||||
#endif
|
||||
33
lib/libc/io/CMakeLists.txt
Normal file
33
lib/libc/io/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
set(source_dirs unistd stdio)
|
||||
|
||||
file(GLOB sources *.c *.h)
|
||||
|
||||
foreach (dir ${source_dirs})
|
||||
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
|
||||
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
|
||||
|
||||
set(sources ${sources} ${dir_sources})
|
||||
set(headers ${headers} ${dir_headers})
|
||||
endforeach (dir)
|
||||
|
||||
file(GLOB_RECURSE sub_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
|
||||
set(headers ${headers} ${sub_headers})
|
||||
|
||||
set(component_sources ${sources} PARENT_SCOPE)
|
||||
set(component_headers ${headers} PARENT_SCOPE)
|
||||
set(component_public_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE)
|
||||
|
||||
rosetta_add_library(STATIC
|
||||
NAME libc-io
|
||||
PUBLIC_INCLUDE_DIRS
|
||||
${public_include_dirs}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libc-io
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libc-io libc-core interface::fs libxpc-static libmango)
|
||||
2
lib/libc/io/fs.c
Normal file
2
lib/libc/io/fs.c
Normal file
@@ -0,0 +1,2 @@
|
||||
#define MSG_IMPLEMENTATION
|
||||
#include <rosetta/fs.h>
|
||||
43
lib/libc/io/include/sys/mman.h
Normal file
43
lib/libc/io/include/sys/mman.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef SYS_MMAN_H_
|
||||
#define SYS_MMAN_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PROT_NONE 0x00u
|
||||
#define PROT_EXEC 0x01u
|
||||
#define PROT_READ 0x02u
|
||||
#define PROT_WRITE 0x04u
|
||||
|
||||
#define MAP_SHARED 0x01u
|
||||
#define MAP_SHARED_VALIDATE 0x03u
|
||||
#define MAP_PRIVATE 0x04u
|
||||
#define MAP_32BIT 0x08u
|
||||
#define MAP_ANON MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS 0x10u
|
||||
#define MAP_DENYWRITE 0x20u
|
||||
#define MAP_EXECUTABLE 0x40u
|
||||
#define MAP_FILE 0x80u
|
||||
#define MAP_FIXED 0x100u
|
||||
#define MAP_FIXED_NOREPLACE 0x300u
|
||||
#define MAP_GROWSDOWN 0x400u
|
||||
#define MAP_HUGETLB 0x800u
|
||||
#define MAP_HUGE_2MB 0x1000u
|
||||
#define MAP_HUGE_1GB 0x2000u
|
||||
#define MAP_LOCKED 0x4000u
|
||||
#define MAP_NONBLOCK 0x8000u
|
||||
#define MAP_NORESERVE 0x10000u
|
||||
#define MAP_POPULATE 0x20000u
|
||||
#define MAP_STACK 0x40000u
|
||||
#define MAP_SYNC 0x80000u
|
||||
#define MAP_UNINITIALIZED 0x100000u
|
||||
|
||||
extern void *mmap(
|
||||
void *addr,
|
||||
size_t length,
|
||||
int prot,
|
||||
int flags,
|
||||
int fd,
|
||||
off_t offset);
|
||||
|
||||
#endif
|
||||
18
lib/libc/io/include/sys/remote.h
Normal file
18
lib/libc/io/include/sys/remote.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef SYS_REMOTE_H_
|
||||
#define SYS_REMOTE_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum sys_remote_id {
|
||||
SYS_REMOTE_NONE,
|
||||
SYS_REMOTE_NSD,
|
||||
};
|
||||
|
||||
extern bool sys_remote_get(
|
||||
enum sys_remote_id id,
|
||||
tid_t *out_tid,
|
||||
unsigned int *out_chid);
|
||||
extern void sys_remote_set(enum sys_remote_id id, tid_t tid, unsigned int chid);
|
||||
|
||||
#endif
|
||||
45
lib/libc/io/remote.c
Normal file
45
lib/libc/io/remote.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <sys/remote.h>
|
||||
|
||||
#define TID_INVALID ((tid_t) - 1)
|
||||
#define CHID_INVALID ((unsigned int)-1)
|
||||
|
||||
struct remote {
|
||||
tid_t tid;
|
||||
unsigned int chid;
|
||||
};
|
||||
|
||||
static struct remote remotes[] = {
|
||||
[SYS_REMOTE_NONE] = {.tid = TID_INVALID, .chid = CHID_INVALID},
|
||||
[SYS_REMOTE_NSD] = {.tid = TID_INVALID, .chid = CHID_INVALID},
|
||||
};
|
||||
static const size_t nr_remotes = sizeof remotes / sizeof remotes[0];
|
||||
|
||||
bool sys_remote_get(
|
||||
enum sys_remote_id id,
|
||||
tid_t *out_tid,
|
||||
unsigned int *out_chid)
|
||||
{
|
||||
if (id < 0 || id >= nr_remotes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct remote *remote = &remotes[id];
|
||||
if (remote->tid == TID_INVALID || remote->chid == CHID_INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_tid = remote->tid;
|
||||
*out_chid = remote->chid;
|
||||
return true;
|
||||
}
|
||||
|
||||
void sys_remote_set(enum sys_remote_id id, tid_t tid, unsigned int chid)
|
||||
{
|
||||
if (id < 0 || id >= nr_remotes) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct remote *remote = &remotes[id];
|
||||
remote->tid = tid;
|
||||
remote->chid = chid;
|
||||
}
|
||||
0
lib/libc/io/unistd/close.c
Normal file
0
lib/libc/io/unistd/close.c
Normal file
6
lib/libc/io/unistd/mmap.c
Normal file
6
lib/libc/io/unistd/mmap.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
40
lib/libc/io/unistd/open.c
Normal file
40
lib/libc/io/unistd/open.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/msg.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <sys/remote.h>
|
||||
|
||||
int open(const char *path, int flags)
|
||||
{
|
||||
tid_t remote_tid;
|
||||
unsigned int remote_chid;
|
||||
if (!sys_remote_get(SYS_REMOTE_NSD, &remote_tid, &remote_chid)) {
|
||||
return __set_errno(ENXIO);
|
||||
}
|
||||
|
||||
kern_handle_t port;
|
||||
kern_status_t status = port_create(&port);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
status = port_connect(port, remote_tid, remote_chid);
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(port);
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
int err = SUCCESS;
|
||||
status = fs_open(port, path, flags, &err);
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(port);
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
if (err != SUCCESS) {
|
||||
kern_handle_close(port);
|
||||
return __set_errno(err);
|
||||
}
|
||||
|
||||
return (int)port;
|
||||
}
|
||||
21
lib/libc/io/unistd/read.c
Normal file
21
lib/libc/io/unistd/read.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/msg.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <sys/remote.h>
|
||||
|
||||
int read(int fd, void *buf, size_t count)
|
||||
{
|
||||
int err;
|
||||
size_t nr_read;
|
||||
kern_status_t status = fs_read(fd, count, &err, &nr_read, buf, count);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
if (err != SUCCESS) {
|
||||
return __set_errno(err);
|
||||
}
|
||||
|
||||
return nr_read;
|
||||
}
|
||||
0
lib/libc/io/unistd/write.c
Normal file
0
lib/libc/io/unistd/write.c
Normal file
33
lib/libc/malloc/CMakeLists.txt
Normal file
33
lib/libc/malloc/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
set(source_dirs stdlib string)
|
||||
|
||||
file(GLOB sources *.c *.h)
|
||||
|
||||
foreach (dir ${source_dirs})
|
||||
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
|
||||
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
|
||||
|
||||
set(sources ${sources} ${dir_sources})
|
||||
set(headers ${headers} ${dir_headers})
|
||||
endforeach (dir)
|
||||
|
||||
file(GLOB_RECURSE sub_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
|
||||
set(headers ${headers} ${sub_headers})
|
||||
|
||||
set(component_sources ${sources} PARENT_SCOPE)
|
||||
set(component_headers ${headers} PARENT_SCOPE)
|
||||
set(component_public_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE)
|
||||
|
||||
rosetta_add_library(STATIC
|
||||
NAME libc-malloc
|
||||
PUBLIC_INCLUDE_DIRS
|
||||
${public_include_dirs}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libc-malloc
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libc-malloc libc-core libmango)
|
||||
112
lib/libc/malloc/heap.c
Normal file
112
lib/libc/malloc/heap.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "liballoc.h"
|
||||
|
||||
#include <heap/heap.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/status.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define HEAP_REGION_SIZE 0x40000000
|
||||
#define HEAP_EXPAND_INCREMENT 0x100000
|
||||
|
||||
void *heap_alloc(heap_t *heap, size_t sz)
|
||||
{
|
||||
return _lbmalloc(heap, sz);
|
||||
}
|
||||
|
||||
void *heap_realloc(heap_t *heap, void *p, size_t sz)
|
||||
{
|
||||
return _lbrealloc(heap, p, sz);
|
||||
}
|
||||
|
||||
void *heap_calloc(heap_t *heap, size_t num, size_t itemsz)
|
||||
{
|
||||
return _lbcalloc(heap, num, itemsz);
|
||||
}
|
||||
|
||||
void heap_free(heap_t *heap, void *p)
|
||||
{
|
||||
_lbfree(heap, p);
|
||||
}
|
||||
|
||||
static kern_status_t init_heap_region(heap_t *heap)
|
||||
{
|
||||
kern_handle_t self, address_space;
|
||||
task_self(&self);
|
||||
task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
|
||||
kern_status_t status = vm_region_create(
|
||||
address_space,
|
||||
"libc-heap",
|
||||
9,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
HEAP_REGION_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&heap->heap_region,
|
||||
&heap->heap_base);
|
||||
|
||||
kern_handle_close(address_space);
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static kern_status_t expand_heap(heap_t *heap)
|
||||
{
|
||||
kern_handle_t vmo;
|
||||
kern_status_t status = vm_object_create(
|
||||
"libc-heap-data",
|
||||
14,
|
||||
HEAP_EXPAND_INCREMENT,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&vmo);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
virt_addr_t base = 0;
|
||||
|
||||
status = vm_region_map_relative(
|
||||
heap->heap_region,
|
||||
heap->heap_sys_alloc,
|
||||
vmo,
|
||||
0,
|
||||
HEAP_EXPAND_INCREMENT,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&base);
|
||||
|
||||
kern_handle_close(vmo);
|
||||
|
||||
heap->heap_sys_alloc += HEAP_EXPAND_INCREMENT;
|
||||
return status;
|
||||
}
|
||||
|
||||
void *heap_expand(heap_t *heap, size_t size)
|
||||
{
|
||||
kern_status_t status = KERN_OK;
|
||||
if (heap->heap_region == KERN_HANDLE_INVALID) {
|
||||
status = init_heap_region(heap);
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (heap->heap_req_alloc + size > heap->heap_sys_alloc) {
|
||||
status = expand_heap(heap);
|
||||
if (status != KERN_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void *p = (void *)(heap->heap_base + heap->heap_req_alloc);
|
||||
heap->heap_req_alloc += size;
|
||||
|
||||
return p;
|
||||
}
|
||||
16
lib/libc/malloc/include/heap/_liballoc.h
Normal file
16
lib/libc/malloc/include/heap/_liballoc.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef HEAP__LIBALLOC_H_
|
||||
#define HEAP__LIBALLOC_H_
|
||||
|
||||
struct liballoc_major;
|
||||
struct liballoc_minor;
|
||||
|
||||
#define HEAP_INIT \
|
||||
{ \
|
||||
.heap_region = KERN_HANDLE_INVALID, \
|
||||
.heap_liballoc = { \
|
||||
.l_pageSize = 4096, \
|
||||
.l_pageCount = 16, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#endif
|
||||
50
lib/libc/malloc/include/heap/heap.h
Normal file
50
lib/libc/malloc/include/heap/heap.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef HEAP_H_
|
||||
#define HEAP_H_
|
||||
|
||||
#include <heap/_liballoc.h>
|
||||
#include <mango/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum heap_result {
|
||||
HEAP_OK = 0,
|
||||
HEAP_ERR_NO_MEMORY,
|
||||
HEAP_ERR_INVALID_ARGUMENT,
|
||||
} heap_result_t;
|
||||
|
||||
typedef struct heap {
|
||||
kern_handle_t heap_region;
|
||||
/* amount of space requested from the heap by the user */
|
||||
size_t heap_req_alloc;
|
||||
/* amount of space requested from the system by the heap */
|
||||
size_t heap_sys_alloc;
|
||||
/* base address of the heap */
|
||||
virt_addr_t heap_base;
|
||||
|
||||
union {
|
||||
struct {
|
||||
struct liballoc_major *l_memRoot;
|
||||
struct liballoc_major *l_bestBet;
|
||||
|
||||
unsigned int l_pageSize;
|
||||
unsigned int l_pageCount;
|
||||
unsigned long long l_allocated;
|
||||
unsigned long long l_inuse;
|
||||
|
||||
long long l_warningCount;
|
||||
long long l_errorCount;
|
||||
long long l_possibleOverruns;
|
||||
} heap_liballoc;
|
||||
};
|
||||
} heap_t;
|
||||
|
||||
extern kern_status_t heap_init(heap_t *heap, size_t size);
|
||||
extern kern_status_t heap_destroy(heap_t *heap);
|
||||
|
||||
extern void *heap_alloc(heap_t *heap, size_t sz);
|
||||
extern void *heap_realloc(heap_t *heap, void *p, size_t sz);
|
||||
extern void *heap_calloc(heap_t *heap, size_t num, size_t itemsz);
|
||||
extern void heap_free(heap_t *heap, void *p);
|
||||
|
||||
extern void *heap_expand(heap_t *heap, size_t size);
|
||||
|
||||
#endif
|
||||
834
lib/libc/malloc/liballoc.c
Normal file
834
lib/libc/malloc/liballoc.c
Normal file
@@ -0,0 +1,834 @@
|
||||
#include "liballoc.h"
|
||||
|
||||
/** Durand's Amazing Super Duper Memory functions. */
|
||||
|
||||
#define VERSION "1.1"
|
||||
#define ALIGNMENT \
|
||||
16ul // 4ul ///< This is the byte alignment
|
||||
// that memory must be allocated on. IMPORTANT for GTK and other
|
||||
// stuff.
|
||||
|
||||
#define ALIGN_TYPE char /// unsigned char[16] /// unsigned short
|
||||
#define ALIGN_INFO \
|
||||
sizeof(ALIGN_TYPE) * 16 ///< Alignment information is stored right
|
||||
///< before the pointer. This is the number of
|
||||
///< bytes of information stored there.
|
||||
|
||||
#define USE_CASE1
|
||||
#define USE_CASE2
|
||||
#define USE_CASE3
|
||||
#define USE_CASE4
|
||||
#define USE_CASE5
|
||||
|
||||
/** This macro will conveniently align our pointer upwards */
|
||||
#define ALIGN(ptr) \
|
||||
if (ALIGNMENT > 1) { \
|
||||
uintptr_t diff; \
|
||||
ptr = (void *)((uintptr_t)ptr + ALIGN_INFO); \
|
||||
diff = (uintptr_t)ptr & (ALIGNMENT - 1); \
|
||||
if (diff != 0) { \
|
||||
diff = ALIGNMENT - diff; \
|
||||
ptr = (void *)((uintptr_t)ptr + diff); \
|
||||
} \
|
||||
*((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)) \
|
||||
= diff + ALIGN_INFO; \
|
||||
}
|
||||
|
||||
#define UNALIGN(ptr) \
|
||||
if (ALIGNMENT > 1) { \
|
||||
uintptr_t diff \
|
||||
= *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)); \
|
||||
if (diff < (ALIGNMENT + ALIGN_INFO)) { \
|
||||
ptr = (void *)((uintptr_t)ptr - diff); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LIBALLOC_MAGIC 0xc001c0de
|
||||
#define LIBALLOC_DEAD 0xdeaddead
|
||||
|
||||
#if defined DEBUG || defined INFO
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FLUSH() fflush(stdout)
|
||||
|
||||
#endif
|
||||
|
||||
/** A structure found at the top of all system allocated
|
||||
* memory blocks. It details the usage of the memory block.
|
||||
*/
|
||||
struct liballoc_major {
|
||||
struct liballoc_major *prev; ///< Linked list information.
|
||||
struct liballoc_major *next; ///< Linked list information.
|
||||
unsigned int pages; ///< The number of pages in the block.
|
||||
unsigned int size; ///< The number of pages in the block.
|
||||
unsigned int usage; ///< The number of bytes used in the block.
|
||||
struct liballoc_minor *first; ///< A pointer to the first allocated
|
||||
///< memory in the block.
|
||||
};
|
||||
|
||||
/** This is a structure found at the beginning of all
|
||||
* sections in a major block which were allocated by a
|
||||
* malloc, calloc, realloc call.
|
||||
*/
|
||||
struct liballoc_minor {
|
||||
struct liballoc_minor *prev; ///< Linked list information.
|
||||
struct liballoc_minor *next; ///< Linked list information.
|
||||
struct liballoc_major *block; ///< The owning block. A pointer to the
|
||||
///< major structure.
|
||||
unsigned int magic; ///< A magic number to idenfity correctness.
|
||||
unsigned int size; ///< The size of the memory allocated. Could be 1
|
||||
///< byte or more.
|
||||
unsigned int req_size; ///< The size of memory requested.
|
||||
};
|
||||
|
||||
#if 0
|
||||
static struct liballoc_major *l_memRoot
|
||||
= NULL; ///< The root memory block acquired from the system.
|
||||
static struct liballoc_major *l_bestBet
|
||||
= NULL; ///< The major with the most free memory.
|
||||
|
||||
static unsigned int l_pageSize
|
||||
= 4096; ///< The size of an individual page. Set up in liballoc_init.
|
||||
static unsigned int l_pageCount = 16; ///< The number of pages to request per
|
||||
///< chunk. Set up in liballoc_init.
|
||||
static unsigned long long l_allocated
|
||||
= 0; ///< Running total of allocated memory.
|
||||
static unsigned long long l_inuse = 0; ///< Running total of used memory.
|
||||
|
||||
static long long l_warningCount = 0; ///< Number of warnings encountered
|
||||
static long long l_errorCount = 0; ///< Number of actual errors
|
||||
static long long l_possibleOverruns = 0; ///< Number of possible overruns
|
||||
#endif
|
||||
|
||||
// *********** HELPER FUNCTIONS *******************************
|
||||
|
||||
static void *liballoc_memset(void *s, int c, size_t n)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < n; i++)
|
||||
((char *)s)[i] = c;
|
||||
|
||||
return s;
|
||||
}
|
||||
static void *liballoc_memcpy(void *s1, const void *s2, size_t n)
|
||||
{
|
||||
char *cdest;
|
||||
char *csrc;
|
||||
unsigned int *ldest = (unsigned int *)s1;
|
||||
unsigned int *lsrc = (unsigned int *)s2;
|
||||
|
||||
while (n >= sizeof(unsigned int)) {
|
||||
*ldest++ = *lsrc++;
|
||||
n -= sizeof(unsigned int);
|
||||
}
|
||||
|
||||
cdest = (char *)ldest;
|
||||
csrc = (char *)lsrc;
|
||||
|
||||
while (n > 0) {
|
||||
*cdest++ = *csrc++;
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
#if defined DEBUG || defined INFO
|
||||
static void liballoc_dump()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
struct liballoc_major *maj = l_memRoot;
|
||||
struct liballoc_minor *min = NULL;
|
||||
#endif
|
||||
|
||||
printf("liballoc: ------ Memory data ---------------\n");
|
||||
printf("liballoc: System memory allocated: %i bytes\n", l_allocated);
|
||||
printf("liballoc: Memory in used (malloc'ed): %i bytes\n", l_inuse);
|
||||
printf("liballoc: Warning count: %i\n", l_warningCount);
|
||||
printf("liballoc: Error count: %i\n", l_errorCount);
|
||||
printf("liballoc: Possible overruns: %i\n", l_possibleOverruns);
|
||||
|
||||
#ifdef DEBUG
|
||||
while (maj != NULL) {
|
||||
printf("liballoc: %x: total = %i, used = %i\n",
|
||||
maj,
|
||||
maj->size,
|
||||
maj->usage);
|
||||
|
||||
min = maj->first;
|
||||
while (min != NULL) {
|
||||
printf("liballoc: %x: %i bytes\n", min, min->size);
|
||||
min = min->next;
|
||||
}
|
||||
|
||||
maj = maj->next;
|
||||
}
|
||||
#endif
|
||||
|
||||
FLUSH();
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CTX(n) (heap->heap_liballoc.n)
|
||||
|
||||
// ***************************************************************
|
||||
|
||||
static struct liballoc_major *allocate_new_page(heap_t *heap, unsigned int size)
|
||||
{
|
||||
unsigned int st;
|
||||
struct liballoc_major *maj;
|
||||
|
||||
// This is how much space is required.
|
||||
st = size + sizeof(struct liballoc_major);
|
||||
st += sizeof(struct liballoc_minor);
|
||||
|
||||
// Perfect amount of space?
|
||||
if ((st % CTX(l_pageSize)) == 0)
|
||||
st = st / (CTX(l_pageSize));
|
||||
else
|
||||
st = st / (CTX(l_pageSize)) + 1;
|
||||
// No, add the buffer.
|
||||
|
||||
// Make sure it's >= the minimum size.
|
||||
if (st < CTX(l_pageCount))
|
||||
st = CTX(l_pageCount);
|
||||
|
||||
maj = (struct liballoc_major *)liballoc_alloc(heap, st);
|
||||
|
||||
if (maj == NULL) {
|
||||
CTX(l_warningCount) += 1;
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: WARNING: liballoc_alloc( %i ) return NULL\n",
|
||||
st);
|
||||
FLUSH();
|
||||
#endif
|
||||
return NULL; // uh oh, we ran out of memory.
|
||||
}
|
||||
|
||||
maj->prev = NULL;
|
||||
maj->next = NULL;
|
||||
maj->pages = st;
|
||||
maj->size = st * CTX(l_pageSize);
|
||||
maj->usage = sizeof(struct liballoc_major);
|
||||
maj->first = NULL;
|
||||
|
||||
CTX(l_allocated) += maj->size;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("liballoc: Resource allocated %x of %i pages (%i bytes) for %i "
|
||||
"size.\n",
|
||||
maj,
|
||||
st,
|
||||
maj->size,
|
||||
size);
|
||||
|
||||
printf("liballoc: Total memory usage = %i KB\n",
|
||||
(int)((l_allocated / (1024))));
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
return maj;
|
||||
}
|
||||
|
||||
void *PREFIX(malloc)(heap_t *heap, size_t req_size)
|
||||
{
|
||||
int startedBet = 0;
|
||||
unsigned long long bestSize = 0;
|
||||
void *p = NULL;
|
||||
uintptr_t diff;
|
||||
struct liballoc_major *maj;
|
||||
struct liballoc_minor *min;
|
||||
struct liballoc_minor *new_min;
|
||||
unsigned long size = req_size;
|
||||
|
||||
// For alignment, we adjust size so there's enough space to align.
|
||||
if (ALIGNMENT > 1) {
|
||||
size += ALIGNMENT + ALIGN_INFO;
|
||||
}
|
||||
// So, ideally, we really want an alignment of 0 or 1 in order
|
||||
// to save space.
|
||||
|
||||
liballoc_lock(heap);
|
||||
|
||||
if (size == 0) {
|
||||
CTX(l_warningCount) += 1;
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: WARNING: alloc( 0 ) called from %x\n",
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(heap);
|
||||
return PREFIX(malloc)(heap, 1);
|
||||
}
|
||||
|
||||
if (CTX(l_memRoot) == NULL) {
|
||||
#if defined DEBUG || defined INFO
|
||||
#ifdef DEBUG
|
||||
printf("liballoc: initialization of liballoc " VERSION "\n");
|
||||
#endif
|
||||
atexit(liballoc_dump);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
// This is the first time we are being used.
|
||||
CTX(l_memRoot) = allocate_new_page(heap, size);
|
||||
if (CTX(l_memRoot) == NULL) {
|
||||
liballoc_unlock(heap);
|
||||
#ifdef DEBUG
|
||||
printf("liballoc: initial l_memRoot initialization "
|
||||
"failed\n",
|
||||
p);
|
||||
FLUSH();
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("liballoc: set up first memory major %x\n", l_memRoot);
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("liballoc: %x PREFIX(malloc)( %i ): ",
|
||||
__builtin_return_address(0),
|
||||
size);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
// Now we need to bounce through every major and find enough space....
|
||||
|
||||
maj = CTX(l_memRoot);
|
||||
startedBet = 0;
|
||||
|
||||
// Start at the best bet....
|
||||
if (CTX(l_bestBet) != NULL) {
|
||||
bestSize = CTX(l_bestBet)->size - CTX(l_bestBet)->usage;
|
||||
|
||||
if (bestSize > (size + sizeof(struct liballoc_minor))) {
|
||||
maj = CTX(l_bestBet);
|
||||
startedBet = 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (maj != NULL) {
|
||||
diff = maj->size - maj->usage;
|
||||
// free memory in the block
|
||||
|
||||
if (bestSize < diff) {
|
||||
// Hmm.. this one has more memory then our bestBet.
|
||||
// Remember!
|
||||
CTX(l_bestBet) = maj;
|
||||
bestSize = diff;
|
||||
}
|
||||
|
||||
#ifdef USE_CASE1
|
||||
|
||||
// CASE 1: There is not enough space in this major block.
|
||||
if (diff < (size + sizeof(struct liballoc_minor))) {
|
||||
#ifdef DEBUG
|
||||
printf("CASE 1: Insufficient space in block %x\n", maj);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
// Another major block next to this one?
|
||||
if (maj->next != NULL) {
|
||||
maj = maj->next; // Hop to that one.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startedBet == 1) // If we started at the best bet,
|
||||
{ // let's start all over again.
|
||||
maj = CTX(l_memRoot);
|
||||
startedBet = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a new major block next to this one and...
|
||||
maj->next = allocate_new_page(
|
||||
heap,
|
||||
size); // next one will be okay.
|
||||
if (maj->next == NULL)
|
||||
break; // no more memory.
|
||||
maj->next->prev = maj;
|
||||
maj = maj->next;
|
||||
|
||||
// .. fall through to CASE 2 ..
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE2
|
||||
|
||||
// CASE 2: It's a brand new block.
|
||||
if (maj->first == NULL) {
|
||||
maj->first
|
||||
= (struct liballoc_minor
|
||||
*)((uintptr_t)maj
|
||||
+ sizeof(struct liballoc_major));
|
||||
|
||||
maj->first->magic = LIBALLOC_MAGIC;
|
||||
maj->first->prev = NULL;
|
||||
maj->first->next = NULL;
|
||||
maj->first->block = maj;
|
||||
maj->first->size = size;
|
||||
maj->first->req_size = req_size;
|
||||
maj->usage += size + sizeof(struct liballoc_minor);
|
||||
|
||||
CTX(l_inuse) += size;
|
||||
|
||||
p = (void *)((uintptr_t)(maj->first)
|
||||
+ sizeof(struct liballoc_minor));
|
||||
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("CASE 2: returning %x\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(heap); // release the lock
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE3
|
||||
|
||||
// CASE 3: Block in use and enough space at the start of the
|
||||
// block.
|
||||
diff = (uintptr_t)(maj->first);
|
||||
diff -= (uintptr_t)maj;
|
||||
diff -= sizeof(struct liballoc_major);
|
||||
|
||||
if (diff >= (size + sizeof(struct liballoc_minor))) {
|
||||
// Yes, space in front. Squeeze in.
|
||||
maj->first->prev
|
||||
= (struct liballoc_minor
|
||||
*)((uintptr_t)maj
|
||||
+ sizeof(struct liballoc_major));
|
||||
maj->first->prev->next = maj->first;
|
||||
maj->first = maj->first->prev;
|
||||
|
||||
maj->first->magic = LIBALLOC_MAGIC;
|
||||
maj->first->prev = NULL;
|
||||
maj->first->block = maj;
|
||||
maj->first->size = size;
|
||||
maj->first->req_size = req_size;
|
||||
maj->usage += size + sizeof(struct liballoc_minor);
|
||||
|
||||
CTX(l_inuse) += size;
|
||||
|
||||
p = (void *)((uintptr_t)(maj->first)
|
||||
+ sizeof(struct liballoc_minor));
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("CASE 3: returning %x\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(heap); // release the lock
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE4
|
||||
|
||||
// CASE 4: There is enough space in this block. But is it
|
||||
// contiguous?
|
||||
min = maj->first;
|
||||
|
||||
// Looping within the block now...
|
||||
while (min != NULL) {
|
||||
// CASE 4.1: End of minors in a block. Space from last
|
||||
// and end?
|
||||
if (min->next == NULL) {
|
||||
// the rest of this block is free... is it big
|
||||
// enough?
|
||||
diff = (uintptr_t)(maj) + maj->size;
|
||||
diff -= (uintptr_t)min;
|
||||
diff -= sizeof(struct liballoc_minor);
|
||||
diff -= min->size;
|
||||
// minus already existing usage..
|
||||
|
||||
if (diff
|
||||
>= (size + sizeof(struct liballoc_minor))) {
|
||||
// yay....
|
||||
min->next
|
||||
= (struct liballoc_minor
|
||||
*)((uintptr_t)min
|
||||
+ sizeof(
|
||||
struct
|
||||
liballoc_minor)
|
||||
+ min->size);
|
||||
min->next->prev = min;
|
||||
min = min->next;
|
||||
min->next = NULL;
|
||||
min->magic = LIBALLOC_MAGIC;
|
||||
min->block = maj;
|
||||
min->size = size;
|
||||
min->req_size = req_size;
|
||||
maj->usage += size
|
||||
+ sizeof(struct
|
||||
liballoc_minor);
|
||||
|
||||
CTX(l_inuse) += size;
|
||||
|
||||
p = (void *)((uintptr_t)min
|
||||
+ sizeof(struct
|
||||
liballoc_minor));
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("CASE 4.1: returning %x\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(
|
||||
heap); // release the lock
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE 4.2: Is there space between two minors?
|
||||
if (min->next != NULL) {
|
||||
// is the difference between here and next big
|
||||
// enough?
|
||||
diff = (uintptr_t)(min->next);
|
||||
diff -= (uintptr_t)min;
|
||||
diff -= sizeof(struct liballoc_minor);
|
||||
diff -= min->size;
|
||||
// minus our existing usage.
|
||||
|
||||
if (diff
|
||||
>= (size + sizeof(struct liballoc_minor))) {
|
||||
// yay......
|
||||
new_min = (struct liballoc_minor
|
||||
*)((uintptr_t)min
|
||||
+ sizeof(
|
||||
struct
|
||||
liballoc_minor)
|
||||
+ min->size);
|
||||
|
||||
new_min->magic = LIBALLOC_MAGIC;
|
||||
new_min->next = min->next;
|
||||
new_min->prev = min;
|
||||
new_min->size = size;
|
||||
new_min->req_size = req_size;
|
||||
new_min->block = maj;
|
||||
min->next->prev = new_min;
|
||||
min->next = new_min;
|
||||
maj->usage += size
|
||||
+ sizeof(struct
|
||||
liballoc_minor);
|
||||
|
||||
CTX(l_inuse) += size;
|
||||
|
||||
p = (void *)((uintptr_t)new_min
|
||||
+ sizeof(struct
|
||||
liballoc_minor));
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("CASE 4.2: returning %x\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
liballoc_unlock(
|
||||
heap); // release the lock
|
||||
return p;
|
||||
}
|
||||
} // min->next != NULL
|
||||
|
||||
min = min->next;
|
||||
} // while min != NULL ...
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE5
|
||||
|
||||
// CASE 5: Block full! Ensure next block and loop.
|
||||
if (maj->next == NULL) {
|
||||
#ifdef DEBUG
|
||||
printf("CASE 5: block full\n");
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
if (startedBet == 1) {
|
||||
maj = CTX(l_memRoot);
|
||||
startedBet = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// we've run out. we need more...
|
||||
maj->next = allocate_new_page(
|
||||
heap,
|
||||
size); // next one guaranteed to be okay
|
||||
if (maj->next == NULL)
|
||||
break; // uh oh, no more memory.....
|
||||
maj->next->prev = maj;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
maj = maj->next;
|
||||
} // while (maj != NULL)
|
||||
|
||||
liballoc_unlock(heap); // release the lock
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("All cases exhausted. No memory available.\n");
|
||||
FLUSH();
|
||||
#endif
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n",
|
||||
size);
|
||||
liballoc_dump();
|
||||
FLUSH();
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PREFIX(free)(heap_t *heap, void *ptr)
|
||||
{
|
||||
struct liballoc_minor *min;
|
||||
struct liballoc_major *maj;
|
||||
|
||||
if (ptr == NULL) {
|
||||
CTX(l_warningCount) += 1;
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: WARNING: PREFIX(free)( NULL ) called from "
|
||||
"%x\n",
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
UNALIGN(ptr);
|
||||
|
||||
liballoc_lock(heap); // lockit
|
||||
|
||||
min = (struct liballoc_minor *)((uintptr_t)ptr
|
||||
- sizeof(struct liballoc_minor));
|
||||
|
||||
if (min->magic != LIBALLOC_MAGIC) {
|
||||
CTX(l_errorCount) += 1;
|
||||
|
||||
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
|
||||
if (((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF))
|
||||
|| ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF))
|
||||
|| ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) {
|
||||
CTX(l_possibleOverruns) += 1;
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: ERROR: Possible 1-3 byte overrun for "
|
||||
"magic %x != %x\n",
|
||||
min->magic,
|
||||
LIBALLOC_MAGIC);
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (min->magic == LIBALLOC_DEAD) {
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: ERROR: multiple PREFIX(free)() "
|
||||
"attempt on %x from %x.\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
} else {
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called "
|
||||
"from %x\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
// being lied to...
|
||||
liballoc_unlock(heap); // release the lock
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("liballoc: %x PREFIX(free)( %x ): ",
|
||||
__builtin_return_address(0),
|
||||
ptr);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
maj = min->block;
|
||||
|
||||
CTX(l_inuse) -= min->size;
|
||||
|
||||
maj->usage -= (min->size + sizeof(struct liballoc_minor));
|
||||
min->magic = LIBALLOC_DEAD; // No mojo.
|
||||
|
||||
if (min->next != NULL)
|
||||
min->next->prev = min->prev;
|
||||
if (min->prev != NULL)
|
||||
min->prev->next = min->next;
|
||||
|
||||
if (min->prev == NULL)
|
||||
maj->first = min->next;
|
||||
// Might empty the block. This was the first
|
||||
// minor.
|
||||
|
||||
// We need to clean up after the majors now....
|
||||
|
||||
if (maj->first == NULL) // Block completely unused.
|
||||
{
|
||||
if (CTX(l_memRoot) == maj)
|
||||
CTX(l_memRoot) = maj->next;
|
||||
if (CTX(l_bestBet) == maj)
|
||||
CTX(l_bestBet) = NULL;
|
||||
if (maj->prev != NULL)
|
||||
maj->prev->next = maj->next;
|
||||
if (maj->next != NULL)
|
||||
maj->next->prev = maj->prev;
|
||||
CTX(l_allocated) -= maj->size;
|
||||
|
||||
liballoc_free(heap, maj, maj->pages);
|
||||
} else {
|
||||
if (CTX(l_bestBet) != NULL) {
|
||||
int bestSize
|
||||
= CTX(l_bestBet)->size - CTX(l_bestBet)->usage;
|
||||
int majSize = maj->size - maj->usage;
|
||||
|
||||
if (majSize > bestSize)
|
||||
CTX(l_bestBet) = maj;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("OK\n");
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
liballoc_unlock(heap); // release the lock
|
||||
}
|
||||
|
||||
void *PREFIX(calloc)(heap_t *heap, size_t nobj, size_t size)
|
||||
{
|
||||
int real_size;
|
||||
void *p;
|
||||
|
||||
real_size = nobj * size;
|
||||
|
||||
p = PREFIX(malloc)(heap, real_size);
|
||||
|
||||
liballoc_memset(p, 0, real_size);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void *PREFIX(realloc)(heap_t *heap, void *p, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
struct liballoc_minor *min;
|
||||
unsigned int real_size;
|
||||
|
||||
// Honour the case of size == 0 => free old and return NULL
|
||||
if (size == 0) {
|
||||
PREFIX(free)(heap, p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// In the case of a NULL pointer, return a simple malloc.
|
||||
if (p == NULL)
|
||||
return PREFIX(malloc)(heap, size);
|
||||
|
||||
// Unalign the pointer if required.
|
||||
ptr = p;
|
||||
UNALIGN(ptr);
|
||||
|
||||
liballoc_lock(heap); // lockit
|
||||
|
||||
min = (struct liballoc_minor *)((uintptr_t)ptr
|
||||
- sizeof(struct liballoc_minor));
|
||||
|
||||
// Ensure it is a valid structure.
|
||||
if (min->magic != LIBALLOC_MAGIC) {
|
||||
CTX(l_errorCount) += 1;
|
||||
|
||||
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
|
||||
if (((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF))
|
||||
|| ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF))
|
||||
|| ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) {
|
||||
CTX(l_possibleOverruns) += 1;
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: ERROR: Possible 1-3 byte overrun for "
|
||||
"magic %x != %x\n",
|
||||
min->magic,
|
||||
LIBALLOC_MAGIC);
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (min->magic == LIBALLOC_DEAD) {
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: ERROR: multiple PREFIX(free)() "
|
||||
"attempt on %x from %x.\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
} else {
|
||||
#if defined DEBUG || defined INFO
|
||||
printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called "
|
||||
"from %x\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
// being lied to...
|
||||
liballoc_unlock(heap); // release the lock
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Definitely a memory block.
|
||||
|
||||
real_size = min->req_size;
|
||||
|
||||
if (real_size >= size) {
|
||||
min->req_size = size;
|
||||
liballoc_unlock(heap);
|
||||
return p;
|
||||
}
|
||||
|
||||
liballoc_unlock(heap);
|
||||
|
||||
// If we got here then we're reallocating to a block bigger than us.
|
||||
ptr = PREFIX(malloc)(heap, size); // We need to allocate new memory
|
||||
liballoc_memcpy(ptr, p, real_size);
|
||||
PREFIX(free)(heap, p);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int liballoc_lock(heap_t *heap)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int liballoc_unlock(heap_t *heap)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *liballoc_alloc(heap_t *heap, size_t sz)
|
||||
{
|
||||
return heap_expand(heap, sz);
|
||||
}
|
||||
|
||||
int liballoc_free(heap_t *heap, void *p, size_t sz)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
76
lib/libc/malloc/liballoc.h
Normal file
76
lib/libc/malloc/liballoc.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef _LIBALLOC_H
|
||||
#define _LIBALLOC_H
|
||||
|
||||
#include <heap/heap.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/** \defgroup ALLOCHOOKS liballoc hooks
|
||||
*
|
||||
* These are the OS specific functions which need to
|
||||
* be implemented on any platform that the library
|
||||
* is expected to work on.
|
||||
*/
|
||||
|
||||
/** @{ */
|
||||
|
||||
// If we are told to not define our own size_t, then we skip the define.
|
||||
// #define _HAVE_UINTPTR_T
|
||||
// typedef unsigned long uintptr_t;
|
||||
|
||||
// This lets you prefix malloc and friends
|
||||
#define PREFIX(func) _lb##func
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** This function is supposed to lock the memory data structures. It
|
||||
* could be as simple as disabling interrupts or acquiring a spinlock.
|
||||
* It's up to you to decide.
|
||||
*
|
||||
* \return 0 if the lock was acquired successfully. Anything else is
|
||||
* failure.
|
||||
*/
|
||||
extern int liballoc_lock(heap_t *);
|
||||
|
||||
/** This function unlocks what was previously locked by the liballoc_lock
|
||||
* function. If it disabled interrupts, it enables interrupts. If it
|
||||
* had acquiried a spinlock, it releases the spinlock. etc.
|
||||
*
|
||||
* \return 0 if the lock was successfully released.
|
||||
*/
|
||||
extern int liballoc_unlock(heap_t *);
|
||||
|
||||
/** This is the hook into the local system which allocates pages. It
|
||||
* accepts an integer parameter which is the number of pages
|
||||
* required. The page size was set up in the liballoc_init function.
|
||||
*
|
||||
* \return NULL if the pages were not allocated.
|
||||
* \return A pointer to the allocated memory.
|
||||
*/
|
||||
extern void *liballoc_alloc(heap_t *, size_t);
|
||||
|
||||
/** This frees previously allocated memory. The void* parameter passed
|
||||
* to the function is the exact same value returned from a previous
|
||||
* liballoc_alloc call.
|
||||
*
|
||||
* The integer value is the number of pages to free.
|
||||
*
|
||||
* \return 0 if the memory was successfully freed.
|
||||
*/
|
||||
extern int liballoc_free(heap_t *, void *, size_t);
|
||||
|
||||
extern void *PREFIX(malloc)(heap_t *, size_t); ///< The standard function.
|
||||
extern void *PREFIX(
|
||||
realloc)(heap_t *, void *, size_t); ///< The standard function.
|
||||
extern void *PREFIX(
|
||||
calloc)(heap_t *, size_t, size_t); ///< The standard function.
|
||||
extern void PREFIX(free)(heap_t *, void *); ///< The standard function.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
44
lib/libc/malloc/stdlib/malloc.c
Normal file
44
lib/libc/malloc/stdlib/malloc.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <heap/heap.h>
|
||||
|
||||
#ifdef ENABLE_GLOBAL_HEAP
|
||||
static heap_t global_heap = HEAP_INIT;
|
||||
|
||||
void *malloc(size_t count)
|
||||
{
|
||||
return heap_alloc(&global_heap, count);
|
||||
}
|
||||
|
||||
void *calloc(size_t count, size_t size)
|
||||
{
|
||||
return heap_calloc(&global_heap, count, size);
|
||||
}
|
||||
|
||||
void *realloc(void *p, size_t count)
|
||||
{
|
||||
return heap_realloc(&global_heap, p, count);
|
||||
}
|
||||
|
||||
void free(void *p)
|
||||
{
|
||||
heap_free(&global_heap, p);
|
||||
}
|
||||
#else
|
||||
void *malloc(size_t count)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *calloc(size_t count, size_t size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *realloc(void *p, size_t count)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free(void *p)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
14
lib/libc/malloc/string/strdup.c
Normal file
14
lib/libc/malloc/string/strdup.c
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char *strdup(char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *out = malloc(len + 1);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(out, s, len + 1);
|
||||
return out;
|
||||
}
|
||||
11
lib/libc/runtime/CMakeLists.txt
Normal file
11
lib/libc/runtime/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB runtime_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_SYSTEM_PROCESSOR}/*.s)
|
||||
set_property(SOURCE ${runtime_sources} PROPERTY LANGUAGE C)
|
||||
|
||||
rosetta_add_object_library(
|
||||
NAME libc-runtime STATIC
|
||||
SOURCES ${runtime_sources})
|
||||
|
||||
sysroot_add_object_library(
|
||||
NAME libc-runtime
|
||||
LIB_DIR /usr/lib)
|
||||
29
lib/libfs/CMakeLists.txt
Normal file
29
lib/libfs/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
file(GLOB sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/interface/*.c)
|
||||
file(GLOB headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/fs/*.h)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME libfs SHARED STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libfs
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
sysroot_add_library(
|
||||
NAME libfs-static
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libfs libmango interface::fs libc libxpc)
|
||||
target_link_libraries(libfs-static libmango interface::fs libc-core libxpc-static)
|
||||
|
||||
set_target_properties(libfs-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE)
|
||||
35
lib/libfs/allocator.c
Normal file
35
lib/libfs/allocator.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <fs/allocator.h>
|
||||
|
||||
void *fs_alloc(struct fs_allocator *alloc, size_t count)
|
||||
{
|
||||
if (alloc->fs_alloc) {
|
||||
return alloc->fs_alloc(alloc, count);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *fs_calloc(struct fs_allocator *alloc, size_t count, size_t sz)
|
||||
{
|
||||
if (alloc->fs_calloc) {
|
||||
return alloc->fs_calloc(alloc, count, sz);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *fs_realloc(struct fs_allocator *alloc, void *p, size_t count)
|
||||
{
|
||||
if (alloc->fs_realloc) {
|
||||
return alloc->fs_realloc(alloc, p, count);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fs_free(struct fs_allocator *alloc, void *p)
|
||||
{
|
||||
if (alloc->fs_free) {
|
||||
alloc->fs_free(alloc, p);
|
||||
}
|
||||
}
|
||||
691
lib/libfs/btree.c
Normal file
691
lib/libfs/btree.c
Normal file
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
The Clear BSD License
|
||||
|
||||
Copyright (c) 2023 Max Wash
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted (subject to the limitations in the disclaimer
|
||||
below) provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
*/
|
||||
|
||||
/* templated AVL binary tree implementation
|
||||
|
||||
this file implements an extensible AVL binary tree data structure.
|
||||
|
||||
the primary rule of an AVL binary tree is that for a given node N,
|
||||
the heights of N's left and right subtrees can differ by at most 1.
|
||||
|
||||
the height of a subtree is the length of the longest path between
|
||||
the root of the subtree and a leaf node, including the root node itself.
|
||||
|
||||
the height of a leaf node is 1.
|
||||
|
||||
when a node is inserted into or deleted from the tree, this rule may
|
||||
be broken, in which the tree must be rotated to restore the balance.
|
||||
|
||||
no more than one rotation is required for any insert operations,
|
||||
while multiple rotations may be required for a delete operation.
|
||||
|
||||
there are four types of rotations that can be applied to a tree:
|
||||
- left rotation
|
||||
- right rotation
|
||||
- double left rotations
|
||||
- double right rotations
|
||||
|
||||
by enforcing the balance rule, for a tree with n nodes, the worst-case
|
||||
performance for insert, delete, and search operations is guaranteed
|
||||
to be O(log n).
|
||||
|
||||
this file intentionally excludes any kind of search function implementation.
|
||||
it is up to the programmer to implement their own tree node type
|
||||
using struct btree_node, and their own search function using struct btree.
|
||||
this allows the programmer to define their own node types with complex
|
||||
non-integer key types. btree.h contains a number of macros to help
|
||||
define these functions. the macros do all the work, you just have to
|
||||
provide a comparator function.
|
||||
*/
|
||||
|
||||
#include "btree.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c)))
|
||||
#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c)))
|
||||
|
||||
#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left))
|
||||
#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right))
|
||||
|
||||
#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right))
|
||||
#define HAS_ONE_CHILD(x) \
|
||||
((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \
|
||||
|| (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)))
|
||||
#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))
|
||||
|
||||
#define HEIGHT(x) ((x) ? (x)->b_height : 0)
|
||||
|
||||
static inline void update_height(struct btree_node *x)
|
||||
{
|
||||
x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1;
|
||||
}
|
||||
|
||||
static inline int bf(struct btree_node *x)
|
||||
{
|
||||
int bf = 0;
|
||||
|
||||
if (!x) {
|
||||
return bf;
|
||||
}
|
||||
|
||||
if (x->b_right) {
|
||||
bf += x->b_right->b_height;
|
||||
}
|
||||
|
||||
if (x->b_left) {
|
||||
bf -= x->b_left->b_height;
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
/* perform a left rotation on a subtree
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
X .
|
||||
/ \
|
||||
. Y
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a left rotation on node X,
|
||||
you will get the following tree:
|
||||
|
||||
Z
|
||||
/ \
|
||||
Y .
|
||||
/ \
|
||||
X .
|
||||
/ \
|
||||
. .
|
||||
|
||||
note that this function does NOT update b_height for the rotated
|
||||
nodes. it is up to you to call update_height_to_root().
|
||||
*/
|
||||
static void rotate_left(struct btree *tree, struct btree_node *x)
|
||||
{
|
||||
struct btree_node *y = x->b_right;
|
||||
|
||||
struct btree_node *p = x->b_parent;
|
||||
|
||||
if (y->b_left) {
|
||||
y->b_left->b_parent = x;
|
||||
}
|
||||
|
||||
x->b_right = y->b_left;
|
||||
|
||||
if (!p) {
|
||||
tree->b_root = y;
|
||||
} else if (x == p->b_left) {
|
||||
p->b_left = y;
|
||||
} else {
|
||||
p->b_right = y;
|
||||
}
|
||||
|
||||
x->b_parent = y;
|
||||
y->b_left = x;
|
||||
y->b_parent = p;
|
||||
}
|
||||
|
||||
static void update_height_to_root(struct btree_node *x)
|
||||
{
|
||||
while (x) {
|
||||
update_height(x);
|
||||
x = x->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform a right rotation on a subtree
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
. X
|
||||
/ \
|
||||
Y .
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a right rotation on node X,
|
||||
you will get the following tree:
|
||||
|
||||
Z
|
||||
/ \
|
||||
. Y
|
||||
/ \
|
||||
. X
|
||||
/ \
|
||||
. .
|
||||
|
||||
note that this function does NOT update b_height for the rotated
|
||||
nodes. it is up to you to call update_height_to_root().
|
||||
*/
|
||||
static void rotate_right(struct btree *tree, struct btree_node *y)
|
||||
{
|
||||
struct btree_node *x = y->b_left;
|
||||
|
||||
struct btree_node *p = y->b_parent;
|
||||
|
||||
if (x->b_right) {
|
||||
x->b_right->b_parent = y;
|
||||
}
|
||||
|
||||
y->b_left = x->b_right;
|
||||
|
||||
if (!p) {
|
||||
tree->b_root = x;
|
||||
} else if (y == p->b_left) {
|
||||
p->b_left = x;
|
||||
} else {
|
||||
p->b_right = x;
|
||||
}
|
||||
|
||||
y->b_parent = x;
|
||||
x->b_right = y;
|
||||
x->b_parent = p;
|
||||
}
|
||||
|
||||
/* for a given node Z, perform a right rotation on Z's right child,
|
||||
followed by a left rotation on Z itself.
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
. X
|
||||
/ \
|
||||
Y .
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a double-left rotation on node Z,
|
||||
you will get the following tree:
|
||||
|
||||
Y
|
||||
/ \
|
||||
/ \
|
||||
Z X
|
||||
/ \ / \
|
||||
. . . .
|
||||
|
||||
note that, unlike rotate_left and rotate_right, this function
|
||||
DOES update b_height for the rotated nodes (since it needs to be
|
||||
done in a certain order).
|
||||
*/
|
||||
static void rotate_double_left(struct btree *tree, struct btree_node *z)
|
||||
{
|
||||
struct btree_node *x = z->b_right;
|
||||
struct btree_node *y = x->b_left;
|
||||
|
||||
rotate_right(tree, x);
|
||||
rotate_left(tree, z);
|
||||
|
||||
update_height(z);
|
||||
update_height(x);
|
||||
|
||||
while (y) {
|
||||
update_height(y);
|
||||
y = y->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* for a given node Z, perform a left rotation on Z's left child,
|
||||
followed by a right rotation on Z itself.
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
X .
|
||||
/ \
|
||||
. Y
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a double-right rotation on node Z,
|
||||
you will get the following tree:
|
||||
|
||||
Y
|
||||
/ \
|
||||
/ \
|
||||
X Z
|
||||
/ \ / \
|
||||
. . . .
|
||||
|
||||
note that, unlike rotate_left and rotate_right, this function
|
||||
DOES update b_height for the rotated nodes (since it needs to be
|
||||
done in a certain order).
|
||||
*/
|
||||
static void rotate_double_right(struct btree *tree, struct btree_node *z)
|
||||
{
|
||||
struct btree_node *x = z->b_left;
|
||||
struct btree_node *y = x->b_right;
|
||||
|
||||
rotate_left(tree, x);
|
||||
rotate_right(tree, z);
|
||||
|
||||
update_height(z);
|
||||
update_height(x);
|
||||
|
||||
while (y) {
|
||||
update_height(y);
|
||||
y = y->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* run after an insert operation. checks that the balance factor
|
||||
of the local subtree is within the range -1 <= BF <= 1. if it
|
||||
is not, rotate the subtree to restore balance.
|
||||
|
||||
note that at most one rotation should be required after a node
|
||||
is inserted into the tree.
|
||||
|
||||
this function depends on all nodes in the tree having
|
||||
correct b_height values.
|
||||
|
||||
@param w the node that was just inserted into the tree
|
||||
*/
|
||||
static void insert_fixup(struct btree *tree, struct btree_node *w)
|
||||
{
|
||||
struct btree_node *z = NULL, *y = NULL, *x = NULL;
|
||||
|
||||
z = w;
|
||||
while (z) {
|
||||
if (bf(z) >= -1 && bf(z) <= 1) {
|
||||
goto next_ancestor;
|
||||
}
|
||||
|
||||
if (IS_LEFT_CHILD(z, y)) {
|
||||
if (IS_LEFT_CHILD(y, x)) {
|
||||
rotate_right(tree, z);
|
||||
update_height_to_root(z);
|
||||
} else {
|
||||
rotate_double_right(tree, z);
|
||||
}
|
||||
} else {
|
||||
if (IS_LEFT_CHILD(y, x)) {
|
||||
rotate_double_left(tree, z);
|
||||
} else {
|
||||
rotate_left(tree, z);
|
||||
update_height_to_root(z);
|
||||
}
|
||||
}
|
||||
|
||||
next_ancestor:
|
||||
x = y;
|
||||
y = z;
|
||||
z = z->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* run after a delete operation. checks that the balance factor
|
||||
of the local subtree is within the range -1 <= BF <= 1. if it
|
||||
is not, rotate the subtree to restore balance.
|
||||
|
||||
note that, unlike insert_fixup, multiple rotations may be required
|
||||
to restore balance after a node is deleted.
|
||||
|
||||
this function depends on all nodes in the tree having
|
||||
correct b_height values.
|
||||
|
||||
@param w one of the following:
|
||||
- the parent of the node that was deleted if the node
|
||||
had no children.
|
||||
- the parent of the node that replaced the deleted node
|
||||
if the deleted node had two children.
|
||||
- the node that replaced the node that was deleted, if
|
||||
the node that was deleted had one child.
|
||||
*/
|
||||
static void delete_fixup(struct btree *tree, struct btree_node *w)
|
||||
{
|
||||
struct btree_node *z = w;
|
||||
|
||||
while (z) {
|
||||
if (bf(z) > 1) {
|
||||
if (bf(z->b_right) >= 0) {
|
||||
rotate_left(tree, z);
|
||||
update_height_to_root(z);
|
||||
} else {
|
||||
rotate_double_left(tree, z);
|
||||
}
|
||||
} else if (bf(z) < -1) {
|
||||
if (bf(z->b_left) <= 0) {
|
||||
rotate_right(tree, z);
|
||||
update_height_to_root(z);
|
||||
} else {
|
||||
rotate_double_right(tree, z);
|
||||
}
|
||||
}
|
||||
|
||||
z = z->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* updates b_height for all nodes between the inserted node and the root
|
||||
of the tree, and calls insert_fixup.
|
||||
|
||||
@param node the node that was just inserted into the tree.
|
||||
*/
|
||||
void btree_insert_fixup(struct btree *tree, struct btree_node *node)
|
||||
{
|
||||
node->b_height = 0;
|
||||
|
||||
struct btree_node *cur = node;
|
||||
while (cur) {
|
||||
update_height(cur);
|
||||
cur = cur->b_parent;
|
||||
}
|
||||
|
||||
insert_fixup(tree, node);
|
||||
}
|
||||
|
||||
/* remove a node from a tree.
|
||||
|
||||
this function assumes that `node` has no children, and therefore
|
||||
doesn't need to be replaced.
|
||||
|
||||
updates b_height for all nodes between `node` and the tree root.
|
||||
|
||||
@param node the node to delete.
|
||||
*/
|
||||
static struct btree_node *remove_node_with_no_children(
|
||||
struct btree *tree,
|
||||
struct btree_node *node)
|
||||
{
|
||||
struct btree_node *w = node->b_parent;
|
||||
struct btree_node *p = node->b_parent;
|
||||
node->b_parent = NULL;
|
||||
|
||||
if (!p) {
|
||||
tree->b_root = NULL;
|
||||
} else if (IS_LEFT_CHILD(p, node)) {
|
||||
p->b_left = NULL;
|
||||
} else {
|
||||
p->b_right = NULL;
|
||||
}
|
||||
|
||||
while (p) {
|
||||
update_height(p);
|
||||
p = p->b_parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* remove a node from a tree.
|
||||
|
||||
this function assumes that `node` has one child.
|
||||
the child of `node` is inherited by `node`'s parent, and `node` is removed.
|
||||
|
||||
updates b_height for all nodes between the node that replaced
|
||||
`node` and the tree root.
|
||||
|
||||
@param node the node to delete.
|
||||
*/
|
||||
static struct btree_node *replace_node_with_one_subtree(
|
||||
struct btree *tree,
|
||||
struct btree_node *node)
|
||||
{
|
||||
struct btree_node *p = node->b_parent;
|
||||
struct btree_node *z = NULL;
|
||||
|
||||
if (HAS_LEFT_CHILD(node)) {
|
||||
z = node->b_left;
|
||||
} else {
|
||||
z = node->b_right;
|
||||
}
|
||||
|
||||
struct btree_node *w = z;
|
||||
if (!p) {
|
||||
tree->b_root = z;
|
||||
} else if (IS_LEFT_CHILD(p, node)) {
|
||||
p->b_left = z;
|
||||
} else if (IS_RIGHT_CHILD(p, node)) {
|
||||
p->b_right = z;
|
||||
}
|
||||
|
||||
z->b_parent = p;
|
||||
|
||||
node->b_parent = NULL;
|
||||
node->b_left = node->b_right = NULL;
|
||||
|
||||
while (z) {
|
||||
update_height(z);
|
||||
z = z->b_parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* remove a node from a tree.
|
||||
|
||||
this function assumes that `node` has two children.
|
||||
find the in-order successor Y of `node` (the largest node in `node`'s left
|
||||
sub-tree), removes `node` from the tree and moves Y to where `node` used to
|
||||
be.
|
||||
|
||||
if Y has a child (it will never have more than one), have Y's parent inherit
|
||||
Y's child.
|
||||
|
||||
updates b_height for all nodes between the deepest node that was modified
|
||||
and the tree root.
|
||||
|
||||
@param z the node to delete.
|
||||
*/
|
||||
static struct btree_node *replace_node_with_two_subtrees(
|
||||
struct btree *tree,
|
||||
struct btree_node *z)
|
||||
{
|
||||
/* x will replace z */
|
||||
struct btree_node *x = z->b_left;
|
||||
|
||||
while (x->b_right) {
|
||||
x = x->b_right;
|
||||
}
|
||||
|
||||
/* y is the node that will replace x (if x has a left child) */
|
||||
struct btree_node *y = x->b_left;
|
||||
|
||||
/* w is the starting point for the height update and fixup */
|
||||
struct btree_node *w = x;
|
||||
if (w->b_parent != z) {
|
||||
w = w->b_parent;
|
||||
}
|
||||
|
||||
if (y) {
|
||||
w = y;
|
||||
}
|
||||
|
||||
if (IS_LEFT_CHILD(x->b_parent, x)) {
|
||||
x->b_parent->b_left = y;
|
||||
} else if (IS_RIGHT_CHILD(x->b_parent, x)) {
|
||||
x->b_parent->b_right = y;
|
||||
}
|
||||
|
||||
if (y) {
|
||||
y->b_parent = x->b_parent;
|
||||
}
|
||||
|
||||
if (IS_LEFT_CHILD(z->b_parent, z)) {
|
||||
z->b_parent->b_left = x;
|
||||
} else if (IS_RIGHT_CHILD(z->b_parent, z)) {
|
||||
z->b_parent->b_right = x;
|
||||
}
|
||||
|
||||
x->b_parent = z->b_parent;
|
||||
x->b_left = z->b_left;
|
||||
x->b_right = z->b_right;
|
||||
|
||||
if (x->b_left) {
|
||||
x->b_left->b_parent = x;
|
||||
}
|
||||
|
||||
if (x->b_right) {
|
||||
x->b_right->b_parent = x;
|
||||
}
|
||||
|
||||
if (!x->b_parent) {
|
||||
tree->b_root = x;
|
||||
}
|
||||
|
||||
struct btree_node *cur = w;
|
||||
while (cur) {
|
||||
update_height(cur);
|
||||
cur = cur->b_parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* delete a node from the tree and re-balance it afterwards */
|
||||
void btree_delete(struct btree *tree, struct btree_node *node)
|
||||
{
|
||||
struct btree_node *w = NULL;
|
||||
|
||||
if (HAS_NO_CHILDREN(node)) {
|
||||
w = remove_node_with_no_children(tree, node);
|
||||
} else if (HAS_ONE_CHILD(node)) {
|
||||
w = replace_node_with_one_subtree(tree, node);
|
||||
} else if (HAS_TWO_CHILDREN(node)) {
|
||||
w = replace_node_with_two_subtrees(tree, node);
|
||||
}
|
||||
|
||||
if (w) {
|
||||
delete_fixup(tree, w);
|
||||
}
|
||||
|
||||
node->b_left = node->b_right = node->b_parent = NULL;
|
||||
}
|
||||
|
||||
struct btree_node *btree_first(struct btree *tree)
|
||||
{
|
||||
/* the first node in the tree is the node with the smallest key.
|
||||
we keep moving left until we can't go any further */
|
||||
struct btree_node *cur = tree->b_root;
|
||||
if (!cur) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (cur->b_left) {
|
||||
cur = cur->b_left;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
struct btree_node *btree_last(struct btree *tree)
|
||||
{
|
||||
/* the first node in the tree is the node with the largest key.
|
||||
we keep moving right until we can't go any further */
|
||||
struct btree_node *cur = tree->b_root;
|
||||
if (!cur) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (cur->b_right) {
|
||||
cur = cur->b_right;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
struct btree_node *btree_next(struct btree_node *node)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* there are two possibilities for the next node:
|
||||
|
||||
1. if `node` has a right sub-tree, every node in this sub-tree is
|
||||
bigger than node. the in-order successor of `node` is the smallest
|
||||
node in this subtree.
|
||||
2. if `node` has no right sub-tree, we've reached the largest node in
|
||||
the sub-tree rooted at `node`. we need to go back to our parent
|
||||
and continue the search elsewhere.
|
||||
*/
|
||||
if (node->b_right) {
|
||||
/* case 1: step into `node`'s right sub-tree and keep going
|
||||
left to find the smallest node */
|
||||
struct btree_node *cur = node->b_right;
|
||||
while (cur->b_left) {
|
||||
cur = cur->b_left;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
/* case 2: keep stepping back up towards the root of the tree.
|
||||
if we encounter a step where we are our parent's left child,
|
||||
we've found a parent with a value larger than us. this parent
|
||||
is the in-order successor of `node` */
|
||||
while (node->b_parent && node->b_parent->b_left != node) {
|
||||
node = node->b_parent;
|
||||
}
|
||||
|
||||
return node->b_parent;
|
||||
}
|
||||
|
||||
struct btree_node *btree_prev(struct btree_node *node)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* there are two possibilities for the previous node:
|
||||
|
||||
1. if `node` has a left sub-tree, every node in this sub-tree is
|
||||
smaller than `node`. the in-order predecessor of `node` is the
|
||||
largest node in this subtree.
|
||||
2. if `node` has no left sub-tree, we've reached the smallest node in
|
||||
the sub-tree rooted at `node`. we need to go back to our parent
|
||||
and continue the search elsewhere.
|
||||
*/
|
||||
if (node->b_left) {
|
||||
/* case 1: step into `node`'s left sub-tree and keep going
|
||||
right to find the largest node */
|
||||
struct btree_node *cur = node->b_left;
|
||||
while (cur->b_right) {
|
||||
cur = cur->b_right;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
/* case 2: keep stepping back up towards the root of the tree.
|
||||
if we encounter a step where we are our parent's right child,
|
||||
we've found a parent with a value smaller than us. this parent
|
||||
is the in-order predecessor of `node`. */
|
||||
while (node->b_parent && node->b_parent->b_right != node) {
|
||||
node = node->b_parent;
|
||||
}
|
||||
|
||||
return node->b_parent;
|
||||
}
|
||||
475
lib/libfs/btree.h
Normal file
475
lib/libfs/btree.h
Normal file
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
The Clear BSD License
|
||||
|
||||
Copyright (c) 2023 Max Wash
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted (subject to the limitations in the disclaimer
|
||||
below) provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
*/
|
||||
|
||||
#ifndef _FS_BTREE_H_
|
||||
#define _FS_BTREE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* if your custom structure contains a struct btree_node (i.e. it can be part of
|
||||
a btree), you can use this macro to convert a struct btree_node* to a
|
||||
your_type*
|
||||
|
||||
@param t the name of your custom type (something that can be passed to
|
||||
offsetof)
|
||||
@param m the name of the struct btree_node member variable within your custom
|
||||
type.
|
||||
@param v the struct btree_node pointer that you wish to convert. if this is
|
||||
NULL, NULL will be returned.
|
||||
*/
|
||||
#define BTREE_CONTAINER(t, m, v) \
|
||||
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
|
||||
|
||||
/* defines a simple node insertion function.
|
||||
this function assumes that your nodes have simple integer keys that can be
|
||||
compared with the usual operators.
|
||||
|
||||
EXAMPLE:
|
||||
if you have a tree node type like this:
|
||||
|
||||
struct my_tree_node {
|
||||
int key;
|
||||
struct btree_node base;
|
||||
}
|
||||
|
||||
You would use the following call to generate an insert function for a tree
|
||||
with this node type:
|
||||
|
||||
BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key,
|
||||
my_tree_node_insert);
|
||||
|
||||
Which would emit a function defined like:
|
||||
|
||||
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
|
||||
*node);
|
||||
|
||||
@param node_type your custom tree node type. usually a structure that
|
||||
contains a struct btree_node member.
|
||||
@param container_node_member the name of the struct btree_node member
|
||||
variable within your custom type.
|
||||
@param container_key_member the name of the key member variable within your
|
||||
custom type.
|
||||
@param function_name the name of the function to generate.
|
||||
*/
|
||||
#define BTREE_DEFINE_SIMPLE_INSERT( \
|
||||
node_type, \
|
||||
container_node_member, \
|
||||
container_key_member, \
|
||||
function_name) \
|
||||
void function_name(struct btree *tree, node_type *node) \
|
||||
{ \
|
||||
if (!tree->b_root) { \
|
||||
tree->b_root = &node->container_node_member; \
|
||||
btree_insert_fixup( \
|
||||
tree, \
|
||||
&node->container_node_member); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
struct btree_node *cur = tree->b_root; \
|
||||
while (1) { \
|
||||
node_type *cur_node = BTREE_CONTAINER( \
|
||||
node_type, \
|
||||
container_node_member, \
|
||||
cur); \
|
||||
struct btree_node *next = NULL; \
|
||||
\
|
||||
if (node->container_key_member \
|
||||
> cur_node->container_key_member) { \
|
||||
next = btree_right(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
btree_put_right( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else if ( \
|
||||
node->container_key_member \
|
||||
< cur_node->container_key_member) { \
|
||||
next = btree_left(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
btree_put_left( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
cur = next; \
|
||||
} \
|
||||
\
|
||||
btree_insert_fixup(tree, &node->container_node_member); \
|
||||
}
|
||||
|
||||
/* defines a node insertion function.
|
||||
this function should be used for trees with complex node keys that cannot be
|
||||
directly compared. a comparator for your keys must be supplied.
|
||||
|
||||
EXAMPLE:
|
||||
if you have a tree node type like this:
|
||||
|
||||
struct my_tree_node {
|
||||
complex_key_t key;
|
||||
struct btree_node base;
|
||||
}
|
||||
|
||||
You would need to define a comparator function or macro with the following
|
||||
signature:
|
||||
|
||||
int my_comparator(struct my_tree_node *a, struct my_tree_node *b);
|
||||
|
||||
Which implements the following:
|
||||
|
||||
return -1 if a < b
|
||||
return 0 if a == b
|
||||
return 1 if a > b
|
||||
|
||||
You would use the following call to generate an insert function for a tree
|
||||
with this node type:
|
||||
|
||||
BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert,
|
||||
my_comparator);
|
||||
|
||||
Which would emit a function defined like:
|
||||
|
||||
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
|
||||
*node);
|
||||
|
||||
@param node_type your custom tree node type. usually a structure that
|
||||
contains a struct btree_node member.
|
||||
@param container_node_member the name of the struct btree_node member
|
||||
variable within your custom type.
|
||||
@param container_key_member the name of the key member variable within your
|
||||
custom type.
|
||||
@param function_name the name of the function to generate.
|
||||
@param comparator the name of a comparator function or functional-macro that
|
||||
conforms to the requirements listed above.
|
||||
*/
|
||||
#define BTREE_DEFINE_INSERT( \
|
||||
node_type, \
|
||||
container_node_member, \
|
||||
container_key_member, \
|
||||
function_name, \
|
||||
comparator) \
|
||||
void function_name(struct btree *tree, node_type *node) \
|
||||
{ \
|
||||
if (!tree->b_root) { \
|
||||
tree->b_root = &node->container_node_member; \
|
||||
btree_insert_fixup( \
|
||||
tree, \
|
||||
&node->container_node_member); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
struct btree_node *cur = tree->b_root; \
|
||||
while (1) { \
|
||||
node_type *cur_node = BTREE_CONTAINER( \
|
||||
node_type, \
|
||||
container_node_member, \
|
||||
cur); \
|
||||
struct btree_node *next = NULL; \
|
||||
int cmp = comparator(node, cur_node); \
|
||||
\
|
||||
if (cmp == 1) { \
|
||||
next = btree_right(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
btree_put_right( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else if (cmp == -1) { \
|
||||
next = btree_left(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
btree_put_left( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
cur = next; \
|
||||
} \
|
||||
\
|
||||
btree_insert_fixup(tree, &node->container_node_member); \
|
||||
}
|
||||
|
||||
/* defines a simple tree search function.
|
||||
this function assumes that your nodes have simple integer keys that can be
|
||||
compared with the usual operators.
|
||||
|
||||
EXAMPLE:
|
||||
if you have a tree node type like this:
|
||||
|
||||
struct my_tree_node {
|
||||
int key;
|
||||
struct btree_node base;
|
||||
}
|
||||
|
||||
You would use the following call to generate a search function for a tree
|
||||
with this node type:
|
||||
|
||||
BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key,
|
||||
my_tree_node_get);
|
||||
|
||||
Which would emit a function defined like:
|
||||
|
||||
static struct my_tree_node *my_tree_node_get(struct btree *tree, int key);
|
||||
|
||||
@param node_type your custom tree node type. usually a structure that
|
||||
contains a struct btree_node member.
|
||||
@param key_type the type name of the key embedded in your custom tree node
|
||||
type. this type must be compatible with the builtin comparison operators.
|
||||
@param container_node_member the name of the struct btree_node member
|
||||
variable within your custom type.
|
||||
@param container_key_member the name of the key member variable within your
|
||||
custom type.
|
||||
@param function_name the name of the function to generate.
|
||||
*/
|
||||
#define BTREE_DEFINE_SIMPLE_GET( \
|
||||
node_type, \
|
||||
key_type, \
|
||||
container_node_member, \
|
||||
container_key_member, \
|
||||
function_name) \
|
||||
node_type *function_name(struct btree *tree, key_type key) \
|
||||
{ \
|
||||
struct btree_node *cur = tree->b_root; \
|
||||
while (cur) { \
|
||||
node_type *cur_node = BTREE_CONTAINER( \
|
||||
node_type, \
|
||||
container_node_member, \
|
||||
cur); \
|
||||
if (key > cur_node->container_key_member) { \
|
||||
cur = btree_right(cur); \
|
||||
} else if (key < cur_node->container_key_member) { \
|
||||
cur = btree_left(cur); \
|
||||
} else { \
|
||||
return cur_node; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
/* perform an in-order traversal of a binary tree
|
||||
|
||||
If you have a tree defined like:
|
||||
|
||||
struct btree my_tree;
|
||||
|
||||
with nodes defined like:
|
||||
|
||||
struct my_tree_node {
|
||||
int key;
|
||||
struct btree_node base;
|
||||
}
|
||||
|
||||
and you want to do something like:
|
||||
|
||||
foreach (struct my_tree_node *node : my_tree) { ... }
|
||||
|
||||
you should use this:
|
||||
|
||||
btree_foreach (struct my_tree_node, node, &my_tree, base) { ... }
|
||||
|
||||
@param iter_type the type name of the iterator variable. this should be the
|
||||
tree's node type, and shouldn't be a pointer.
|
||||
@param iter_name the name of the iterator variable.
|
||||
@param tree_name a pointer to the tree to traverse.
|
||||
@param node_member the name of the struct btree_node member variable within
|
||||
the tree node type.
|
||||
*/
|
||||
#define btree_foreach(iter_type, iter_name, tree_name, node_member) \
|
||||
for (iter_type *iter_name = BTREE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
btree_first(tree_name)); \
|
||||
iter_name; \
|
||||
iter_name = BTREE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
btree_next(&((iter_name)->node_member))))
|
||||
|
||||
/* perform an reverse in-order traversal of a binary tree
|
||||
|
||||
If you have a tree defined like:
|
||||
|
||||
struct btree my_tree;
|
||||
|
||||
with nodes defined like:
|
||||
|
||||
struct my_tree_node {
|
||||
int key;
|
||||
struct btree_node base;
|
||||
}
|
||||
|
||||
and you want to do something like:
|
||||
|
||||
foreach (struct my_tree_node *node : reverse(my_tree)) { ... }
|
||||
|
||||
you should use this:
|
||||
|
||||
btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... }
|
||||
|
||||
@param iter_type the type name of the iterator variable. this should be the
|
||||
tree's node type, and shouldn't be a pointer.
|
||||
@param iter_name the name of the iterator variable.
|
||||
@param tree_name a pointer to the tree to traverse.
|
||||
@param node_member the name of the struct btree_node member variable within
|
||||
the tree node type.
|
||||
*/
|
||||
#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \
|
||||
for (iter_type *iter_name \
|
||||
= BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \
|
||||
iter_name; \
|
||||
iter_name = BTREE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
btree_prev(&((iter_name)->node_member))))
|
||||
|
||||
/* binary tree nodes. this *cannot* be used directly. you need to define a
|
||||
custom node type that contains a member variable of type struct btree_node.
|
||||
|
||||
you would then use the supplied macros to define functions to manipulate your
|
||||
custom binary tree.
|
||||
*/
|
||||
struct btree_node {
|
||||
struct btree_node *b_parent, *b_left, *b_right;
|
||||
unsigned short b_height;
|
||||
};
|
||||
|
||||
/* binary tree. unlike struct btree_node, you can define variables of type
|
||||
* struct btree. */
|
||||
struct btree {
|
||||
struct btree_node *b_root;
|
||||
};
|
||||
|
||||
/* re-balance a binary tree after an insertion operation.
|
||||
|
||||
NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or
|
||||
similar, this function will automatically called for you.
|
||||
|
||||
@param tree the tree to re-balance.
|
||||
@param node the node that was just inserted into the tree.
|
||||
*/
|
||||
extern void btree_insert_fixup(struct btree *tree, struct btree_node *node);
|
||||
|
||||
/* delete a node from a binary tree and re-balance the tree afterwards.
|
||||
|
||||
@param tree the tree to delete from
|
||||
@param node the node to delete.
|
||||
*/
|
||||
extern void btree_delete(struct btree *tree, struct btree_node *node);
|
||||
|
||||
/* get the first node in a binary tree.
|
||||
|
||||
this will be the node with the smallest key (i.e. the node that is
|
||||
furthest-left from the root)
|
||||
*/
|
||||
extern struct btree_node *btree_first(struct btree *tree);
|
||||
|
||||
/* get the last node in a binary tree.
|
||||
|
||||
this will be the node with the largest key (i.e. the node that is
|
||||
furthest-right from the root)
|
||||
*/
|
||||
extern struct btree_node *btree_last(struct btree *tree);
|
||||
/* for any binary tree node, this function returns the node with the
|
||||
* next-largest key value */
|
||||
extern struct btree_node *btree_next(struct btree_node *node);
|
||||
/* for any binary tree node, this function returns the node with the
|
||||
* next-smallest key value */
|
||||
extern struct btree_node *btree_prev(struct btree_node *node);
|
||||
|
||||
static inline bool btree_empty(const struct btree *tree)
|
||||
{
|
||||
return tree->b_root == NULL;
|
||||
}
|
||||
|
||||
/* sets `child` as the immediate left-child of `parent` */
|
||||
static inline void btree_put_left(
|
||||
struct btree_node *parent,
|
||||
struct btree_node *child)
|
||||
{
|
||||
parent->b_left = child;
|
||||
child->b_parent = parent;
|
||||
}
|
||||
|
||||
/* sets `child` as the immediate right-child of `parent` */
|
||||
static inline void btree_put_right(
|
||||
struct btree_node *parent,
|
||||
struct btree_node *child)
|
||||
{
|
||||
parent->b_right = child;
|
||||
child->b_parent = parent;
|
||||
}
|
||||
|
||||
/* get the immediate left-child of `node` */
|
||||
static inline struct btree_node *btree_left(struct btree_node *node)
|
||||
{
|
||||
return node->b_left;
|
||||
}
|
||||
|
||||
/* get the immediate right-child of `node` */
|
||||
static inline struct btree_node *btree_right(struct btree_node *node)
|
||||
{
|
||||
return node->b_right;
|
||||
}
|
||||
|
||||
/* get the immediate parent of `node` */
|
||||
static inline struct btree_node *btree_parent(struct btree_node *node)
|
||||
{
|
||||
return node->b_parent;
|
||||
}
|
||||
|
||||
/* get the height of `node`.
|
||||
|
||||
the height of a node is defined as the length of the longest path
|
||||
between the node and a leaf node.
|
||||
|
||||
this count includes the node itself, so the height of a leaf node will be 1.
|
||||
*/
|
||||
static inline unsigned short btree_height(struct btree_node *node)
|
||||
{
|
||||
return node->b_height;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
194
lib/libfs/context.c
Normal file
194
lib/libfs/context.c
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "btree.h"
|
||||
#include "file.h"
|
||||
#include "interface.h"
|
||||
|
||||
#include <fs/allocator.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/dentry.h>
|
||||
#include <fs/inode.h>
|
||||
#include <fs/status.h>
|
||||
#include <fs/superblock.h>
|
||||
#include <mango/log.h>
|
||||
#include <stdio.h>
|
||||
|
||||
BTREE_DEFINE_SIMPLE_GET(struct fs_file, unsigned long, f_node, f_id, get_file);
|
||||
BTREE_DEFINE_SIMPLE_INSERT(struct fs_file, f_node, f_id, put_file);
|
||||
|
||||
struct fs_context {
|
||||
struct fs_superblock *ctx_sb;
|
||||
struct fs_allocator *ctx_alloc;
|
||||
struct btree ctx_filelist;
|
||||
|
||||
struct fs_vtable ctx_vtable;
|
||||
};
|
||||
|
||||
struct fs_context *fs_context_create(struct fs_allocator *alloc)
|
||||
{
|
||||
struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx);
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ctx, 0x0, sizeof *ctx);
|
||||
|
||||
ctx->ctx_alloc = alloc;
|
||||
|
||||
ctx->ctx_vtable.open = fs_msg_open;
|
||||
ctx->ctx_vtable.read = fs_msg_read;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void fs_context_destroy(struct fs_context *ctx)
|
||||
{
|
||||
fs_free(ctx->ctx_alloc, ctx);
|
||||
}
|
||||
|
||||
enum fs_status fs_context_mount_filesystem(
|
||||
struct fs_context *ctx,
|
||||
fs_mount_function_t func,
|
||||
void *arg,
|
||||
enum fs_mount_flags flags)
|
||||
{
|
||||
if (!func) {
|
||||
return FS_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
struct fs_superblock *sb = NULL;
|
||||
enum fs_status status = func(ctx, arg, flags, &sb);
|
||||
if (status != FS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!sb) {
|
||||
return FS_ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
ctx->ctx_sb = sb;
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx)
|
||||
{
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id)
|
||||
{
|
||||
struct fs_file *f = get_file(&ctx->ctx_filelist, id);
|
||||
if (!f) {
|
||||
f = fs_alloc(ctx->ctx_alloc, sizeof *f);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(f, 0x0, sizeof *f);
|
||||
|
||||
f->f_id = id;
|
||||
put_file(&ctx->ctx_filelist, f);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
struct fs_file *fs_context_get_file(struct fs_context *ctx, unsigned long id)
|
||||
{
|
||||
return get_file(&ctx->ctx_filelist, id);
|
||||
}
|
||||
|
||||
void fs_context_close_file(struct fs_context *ctx, struct fs_file *f)
|
||||
{
|
||||
}
|
||||
|
||||
static size_t get_first_path_component(const char *in, char *out, size_t max)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < max - 1) {
|
||||
if (in[i] == '\0' || in[i] == '/') {
|
||||
break;
|
||||
}
|
||||
|
||||
out[i] = in[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
out[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
|
||||
extern enum fs_status fs_context_resolve_path(
|
||||
struct fs_context *ctx,
|
||||
const char *path,
|
||||
struct fs_dentry **out)
|
||||
{
|
||||
if (!ctx->ctx_sb || !ctx->ctx_sb->s_root) {
|
||||
return FS_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
struct fs_dentry *cur = ctx->ctx_sb->s_root;
|
||||
|
||||
char tok[256];
|
||||
|
||||
while (*path != '\0') {
|
||||
while (*path == '/') {
|
||||
path++;
|
||||
}
|
||||
|
||||
size_t tok_len
|
||||
= get_first_path_component(path, tok, sizeof tok);
|
||||
|
||||
if (!tok_len) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_dir = *(path + tok_len) != '\0';
|
||||
|
||||
if (cur->d_inode->i_mode != FS_INODE_DIR) {
|
||||
return FS_ERR_NOT_DIRECTORY;
|
||||
}
|
||||
|
||||
struct fs_dentry *next = NULL;
|
||||
enum fs_status status
|
||||
= fs_inode_lookup(cur->d_inode, tok, &next);
|
||||
|
||||
if (status != FS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!next) {
|
||||
return FS_ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
cur = next;
|
||||
|
||||
path += tok_len;
|
||||
}
|
||||
|
||||
*out = cur;
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
kern_status_t fs_context_dispatch_msg(struct fs_context *ctx, xpc_msg_t *msg)
|
||||
{
|
||||
return fs_dispatch(NULL, msg, &ctx->ctx_vtable, ctx);
|
||||
}
|
||||
|
||||
void *fs_context_alloc(struct fs_context *ctx, size_t count)
|
||||
{
|
||||
return fs_alloc(ctx->ctx_alloc, count);
|
||||
}
|
||||
|
||||
void *fs_context_calloc(struct fs_context *ctx, size_t count, size_t sz)
|
||||
{
|
||||
return fs_calloc(ctx->ctx_alloc, count, sz);
|
||||
}
|
||||
|
||||
void *fs_context_realloc(struct fs_context *ctx, void *p, size_t count)
|
||||
{
|
||||
return fs_realloc(ctx->ctx_alloc, p, count);
|
||||
}
|
||||
|
||||
void fs_context_free(struct fs_context *ctx, void *p)
|
||||
{
|
||||
fs_free(ctx->ctx_alloc, p);
|
||||
}
|
||||
41
lib/libfs/file.c
Normal file
41
lib/libfs/file.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "file.h"
|
||||
|
||||
struct fs_inode *fs_file_get_inode(const struct fs_file *f)
|
||||
{
|
||||
return f->f_inode;
|
||||
}
|
||||
|
||||
size_t fs_file_get_cursor(const struct fs_file *f)
|
||||
{
|
||||
return f->f_seek;
|
||||
}
|
||||
|
||||
enum fs_status fs_file_read(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (!f->f_ops || !f->f_ops->f_read) {
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
off_t seek = f->f_seek;
|
||||
enum fs_status status = f->f_ops->f_read(f, buf, count, &seek);
|
||||
f->f_seek = seek;
|
||||
return status;
|
||||
}
|
||||
|
||||
enum fs_status fs_file_write(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (!f->f_ops || !f->f_ops->f_write) {
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
off_t seek = f->f_seek;
|
||||
enum fs_status status = f->f_ops->f_write(f, buf, count, &seek);
|
||||
f->f_seek = seek;
|
||||
return status;
|
||||
}
|
||||
23
lib/libfs/file.h
Normal file
23
lib/libfs/file.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _FS_FILE_H_
|
||||
#define _FS_FILE_H_
|
||||
|
||||
#include "btree.h"
|
||||
|
||||
#include <fs/dentry.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/inode.h>
|
||||
|
||||
struct fs_file {
|
||||
/* id of the open file, equal to the koid of the port being used to
|
||||
* access the file */
|
||||
unsigned long f_id;
|
||||
struct btree_node f_node;
|
||||
|
||||
const struct fs_file_ops *f_ops;
|
||||
|
||||
off_t f_seek;
|
||||
struct fs_inode *f_inode;
|
||||
struct fs_dentry *f_dent;
|
||||
};
|
||||
|
||||
#endif
|
||||
19
lib/libfs/include/fs/allocator.h
Normal file
19
lib/libfs/include/fs/allocator.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef FS_ALLOCATOR_H_
|
||||
#define FS_ALLOCATOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct fs_allocator {
|
||||
void *fs_arg;
|
||||
void *(*fs_alloc)(struct fs_allocator *, size_t);
|
||||
void *(*fs_calloc)(struct fs_allocator *, size_t, size_t);
|
||||
void *(*fs_realloc)(struct fs_allocator *, void *, size_t);
|
||||
void (*fs_free)(struct fs_allocator *, void *);
|
||||
};
|
||||
|
||||
extern void *fs_alloc(struct fs_allocator *alloc, size_t count);
|
||||
extern void *fs_calloc(struct fs_allocator *alloc, size_t count, size_t sz);
|
||||
extern void *fs_realloc(struct fs_allocator *alloc, void *p, size_t count);
|
||||
extern void fs_free(struct fs_allocator *alloc, void *p);
|
||||
|
||||
#endif
|
||||
55
lib/libfs/include/fs/context.h
Normal file
55
lib/libfs/include/fs/context.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef FS_CONTEXT_H_
|
||||
#define FS_CONTEXT_H_
|
||||
|
||||
#include <rosetta/fs.h>
|
||||
#include <xpc/msg.h>
|
||||
|
||||
struct fs_file;
|
||||
struct fs_dentry;
|
||||
struct fs_context;
|
||||
struct fs_allocator;
|
||||
struct fs_superblock;
|
||||
|
||||
enum fs_mount_flags {
|
||||
FS_MOUNT_READONLY = 0x01u,
|
||||
};
|
||||
|
||||
typedef enum fs_status (*fs_mount_function_t)(
|
||||
struct fs_context *,
|
||||
void *arg,
|
||||
enum fs_mount_flags,
|
||||
struct fs_superblock **);
|
||||
|
||||
extern struct fs_context *fs_context_create(struct fs_allocator *alloc);
|
||||
extern void fs_context_destroy(struct fs_context *ctx);
|
||||
|
||||
extern enum fs_status fs_context_mount_filesystem(
|
||||
struct fs_context *ctx,
|
||||
fs_mount_function_t func,
|
||||
void *arg,
|
||||
enum fs_mount_flags flags);
|
||||
extern enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx);
|
||||
|
||||
extern struct fs_file *fs_context_open_file(
|
||||
struct fs_context *ctx,
|
||||
unsigned long id);
|
||||
extern struct fs_file *fs_context_get_file(
|
||||
struct fs_context *ctx,
|
||||
unsigned long id);
|
||||
extern void fs_context_close_file(struct fs_context *ctx, struct fs_file *f);
|
||||
|
||||
extern enum fs_status fs_context_resolve_path(
|
||||
struct fs_context *ctx,
|
||||
const char *path,
|
||||
struct fs_dentry **out);
|
||||
|
||||
extern kern_status_t fs_context_dispatch_msg(
|
||||
struct fs_context *ctx,
|
||||
xpc_msg_t *msg);
|
||||
|
||||
extern void *fs_context_alloc(struct fs_context *ctx, size_t count);
|
||||
extern void *fs_context_calloc(struct fs_context *ctx, size_t count, size_t sz);
|
||||
extern void *fs_context_realloc(struct fs_context *ctx, void *p, size_t count);
|
||||
extern void fs_context_free(struct fs_context *ctx, void *p);
|
||||
|
||||
#endif
|
||||
19
lib/libfs/include/fs/dentry.h
Normal file
19
lib/libfs/include/fs/dentry.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef FS_DENTRY_H_
|
||||
#define FS_DENTRY_H_
|
||||
|
||||
struct fs_inode;
|
||||
struct fs_superblock;
|
||||
|
||||
struct fs_dentry_ops {
|
||||
};
|
||||
|
||||
struct fs_dentry {
|
||||
struct fs_inode *d_inode;
|
||||
struct fs_dentry *d_parent;
|
||||
struct fs_superblock *d_sb;
|
||||
const struct fs_dentry_ops *d_ops;
|
||||
void *d_fsdata;
|
||||
char *d_name;
|
||||
};
|
||||
|
||||
#endif
|
||||
35
lib/libfs/include/fs/file.h
Normal file
35
lib/libfs/include/fs/file.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef FS_FILE_H_
|
||||
#define FS_FILE_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct fs_file;
|
||||
struct xpc_buffer;
|
||||
|
||||
struct fs_file_ops {
|
||||
enum fs_status (*f_read)(
|
||||
struct fs_file *,
|
||||
struct xpc_buffer *,
|
||||
size_t,
|
||||
off_t *);
|
||||
enum fs_status (*f_write)(
|
||||
struct fs_file *,
|
||||
const struct xpc_buffer *,
|
||||
size_t,
|
||||
off_t *);
|
||||
enum fs_status (*f_seek)(struct fs_file *, off_t, int);
|
||||
};
|
||||
|
||||
extern struct fs_inode *fs_file_get_inode(const struct fs_file *f);
|
||||
extern size_t fs_file_get_cursor(const struct fs_file *f);
|
||||
extern enum fs_status fs_file_read(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
size_t count);
|
||||
extern enum fs_status fs_file_write(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
size_t count);
|
||||
|
||||
#endif
|
||||
37
lib/libfs/include/fs/inode.h
Normal file
37
lib/libfs/include/fs/inode.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef FS_INODE_H_
|
||||
#define FS_INODE_H_
|
||||
|
||||
#include <fs/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct fs_inode;
|
||||
struct fs_dentry;
|
||||
struct fs_superblock;
|
||||
struct fs_file_ops;
|
||||
|
||||
enum fs_inode_mode {
|
||||
FS_INODE_REG = 0x01u,
|
||||
FS_INODE_DIR = 0x02u,
|
||||
};
|
||||
|
||||
struct fs_inode_ops {
|
||||
enum fs_status (*i_lookup)(
|
||||
struct fs_inode *,
|
||||
const char *,
|
||||
struct fs_dentry **);
|
||||
};
|
||||
|
||||
struct fs_inode {
|
||||
enum fs_inode_mode i_mode;
|
||||
struct fs_superblock *i_sb;
|
||||
const struct fs_inode_ops *i_ops;
|
||||
const struct fs_file_ops *i_fops;
|
||||
size_t i_size;
|
||||
};
|
||||
|
||||
extern enum fs_status fs_inode_lookup(
|
||||
struct fs_inode *inode,
|
||||
const char *name,
|
||||
struct fs_dentry **out);
|
||||
|
||||
#endif
|
||||
18
lib/libfs/include/fs/status.h
Normal file
18
lib/libfs/include/fs/status.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef FS_STATUS_H_
|
||||
#define FS_STATUS_H_
|
||||
|
||||
enum fs_status {
|
||||
FS_SUCCESS = 0,
|
||||
FS_ERR_NO_ENTRY,
|
||||
FS_ERR_NO_MEMORY,
|
||||
FS_ERR_INVALID_ARGUMENT,
|
||||
FS_ERR_NOT_IMPLEMENTED,
|
||||
FS_ERR_IS_DIRECTORY,
|
||||
FS_ERR_NOT_DIRECTORY,
|
||||
FS_ERR_BAD_STATE,
|
||||
FS_ERR_INTERNAL_FAILURE,
|
||||
};
|
||||
|
||||
extern int fs_status_to_errno(enum fs_status status);
|
||||
|
||||
#endif
|
||||
20
lib/libfs/include/fs/superblock.h
Normal file
20
lib/libfs/include/fs/superblock.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef FS_SUPERBLOCK_H_
|
||||
#define FS_SUPERBLOCK_H_
|
||||
|
||||
struct fs_inode;
|
||||
struct fs_dentry;
|
||||
struct fs_superblock;
|
||||
|
||||
struct fs_superblock_ops {
|
||||
struct fs_inode *(*s_alloc_inode)(struct fs_superblock *);
|
||||
};
|
||||
|
||||
struct fs_superblock {
|
||||
const struct fs_superblock_ops *s_ops;
|
||||
|
||||
struct fs_dentry *s_root;
|
||||
};
|
||||
|
||||
extern struct fs_inode *fs_superblock_alloc_inode(struct fs_superblock *sb);
|
||||
|
||||
#endif
|
||||
13
lib/libfs/inode.c
Normal file
13
lib/libfs/inode.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <fs/inode.h>
|
||||
|
||||
enum fs_status fs_inode_lookup(
|
||||
struct fs_inode *inode,
|
||||
const char *name,
|
||||
struct fs_dentry **out)
|
||||
{
|
||||
if (!inode->i_ops || !inode->i_ops->i_lookup) {
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return inode->i_ops->i_lookup(inode, name, out);
|
||||
}
|
||||
35
lib/libfs/interface.h
Normal file
35
lib/libfs/interface.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _FS_INTERFACE_H_
|
||||
#define _FS_INTERFACE_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <xpc/buffer.h>
|
||||
#include <xpc/context.h>
|
||||
#include <xpc/endpoint.h>
|
||||
#include <xpc/string.h>
|
||||
|
||||
struct msg_endpoint;
|
||||
|
||||
extern kern_status_t fs_msg_open(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
const xpc_string_t *path,
|
||||
int flags,
|
||||
int *out_err,
|
||||
void *arg);
|
||||
extern kern_status_t fs_msg_close(
|
||||
const struct msg_endpoint *sender,
|
||||
const char *path,
|
||||
int flags,
|
||||
int *out_err,
|
||||
void *arg);
|
||||
|
||||
extern kern_status_t fs_msg_read(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
size_t count,
|
||||
int *out_err,
|
||||
size_t *out_nr_read,
|
||||
xpc_buffer_t *out_data,
|
||||
void *arg);
|
||||
|
||||
#endif
|
||||
53
lib/libfs/interface/open.c
Normal file
53
lib/libfs/interface/open.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "../file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
|
||||
extern kern_status_t fs_msg_open(
|
||||
xpc_context_t *xpc,
|
||||
xpc_endpoint_t *sender,
|
||||
const xpc_string_t *path,
|
||||
int flags,
|
||||
int *out_err,
|
||||
void *arg)
|
||||
{
|
||||
char path_buf[4096];
|
||||
size_t path_len = 0;
|
||||
kern_status_t status
|
||||
= xpc_string_read(path, path_buf, sizeof path_buf, &path_len);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
struct fs_context *ctx = arg;
|
||||
|
||||
struct fs_file *f = fs_context_open_file(ctx, sender->e_port);
|
||||
if (!f) {
|
||||
*out_err = ENOMEM;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
if (f->f_inode) {
|
||||
*out_err = EBUSY;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
struct fs_dentry *dent = NULL;
|
||||
enum fs_status fs_status
|
||||
= fs_context_resolve_path(ctx, path_buf, &dent);
|
||||
if (fs_status != FS_SUCCESS) {
|
||||
fs_context_close_file(ctx, f);
|
||||
*out_err = fs_status_to_errno(status);
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
f->f_seek = 0;
|
||||
f->f_dent = dent;
|
||||
f->f_inode = dent->d_inode;
|
||||
f->f_ops = dent->d_inode->i_fops;
|
||||
|
||||
*out_err = SUCCESS;
|
||||
return KERN_OK;
|
||||
}
|
||||
30
lib/libfs/interface/read.c
Normal file
30
lib/libfs/interface/read.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
|
||||
extern kern_status_t fs_msg_read(
|
||||
xpc_context_t *xpc,
|
||||
xpc_endpoint_t *sender,
|
||||
size_t count,
|
||||
int *out_err,
|
||||
size_t *out_nr_read,
|
||||
xpc_buffer_t *out_data,
|
||||
void *arg)
|
||||
{
|
||||
struct fs_context *ctx = arg;
|
||||
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
|
||||
if (!f) {
|
||||
*out_err = EBADF;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
size_t start = fs_file_get_cursor(f);
|
||||
enum fs_status status = fs_file_read(f, out_data, count);
|
||||
size_t end = fs_file_get_cursor(f);
|
||||
|
||||
*out_err = fs_status_to_errno(status);
|
||||
*out_nr_read = end - start;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
25
lib/libfs/status.c
Normal file
25
lib/libfs/status.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <errno.h>
|
||||
#include <fs/status.h>
|
||||
|
||||
int fs_status_to_errno(enum fs_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case FS_SUCCESS:
|
||||
return SUCCESS;
|
||||
case FS_ERR_NO_ENTRY:
|
||||
return ENOENT;
|
||||
case FS_ERR_NO_MEMORY:
|
||||
return ENOMEM;
|
||||
case FS_ERR_INVALID_ARGUMENT:
|
||||
return EINVAL;
|
||||
case FS_ERR_NOT_IMPLEMENTED:
|
||||
return ENOSYS;
|
||||
case FS_ERR_IS_DIRECTORY:
|
||||
return EISDIR;
|
||||
case FS_ERR_NOT_DIRECTORY:
|
||||
return ENOTDIR;
|
||||
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
11
lib/libfs/superblock.c
Normal file
11
lib/libfs/superblock.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <fs/superblock.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct fs_inode *fs_superblock_alloc_inode(struct fs_superblock *sb)
|
||||
{
|
||||
if (!sb->s_ops->s_alloc_inode) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sb->s_ops->s_alloc_inode(sb);
|
||||
}
|
||||
@@ -18,4 +18,4 @@ sysroot_add_library(
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(liblaunch libmango ulibc)
|
||||
target_link_libraries(liblaunch librosetta libmango libc-core)
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
/* TODO in case we ever support ELF32 images */
|
||||
#define elf_class_bits(x) (64)
|
||||
|
||||
#define PAGE_SIZE (page_size())
|
||||
#define PAGE_MASK (page_size() - 1)
|
||||
#define PAGE_SIZE (image->e_page_size)
|
||||
#define PAGE_MASK (image->e_page_size - 1)
|
||||
#define PAGE_OFFSET(v) ((v) & (PAGE_SIZE - 1))
|
||||
#define PAGE_ALIGN_DOWN(v) (v) &= ~(PAGE_SIZE - 1)
|
||||
#define PAGE_ALIGN_UP(v) \
|
||||
@@ -35,16 +35,6 @@
|
||||
|
||||
#undef DEBUG_LOG
|
||||
|
||||
static size_t page_size(void)
|
||||
{
|
||||
static size_t pagesz = 0;
|
||||
if (pagesz == 0) {
|
||||
kern_config_get(KERN_CFG_PAGE_SIZE, &pagesz, sizeof pagesz);
|
||||
}
|
||||
|
||||
return pagesz;
|
||||
}
|
||||
|
||||
static enum launch_status elf_validate_ehdr(elf_ehdr_t *hdr)
|
||||
{
|
||||
if (hdr->e_ident[EI_MAG0] != ELF_MAG0) {
|
||||
@@ -104,6 +94,7 @@ static enum launch_status parse_phdr(struct elf_image *image)
|
||||
size_t r = 0;
|
||||
image->e_total_size = 0;
|
||||
image->e_data_size = 0;
|
||||
off_t vaddr, vlimit;
|
||||
|
||||
for (size_t i = 0; i < image->e_hdr.e_phnum; i++) {
|
||||
off_t offset
|
||||
@@ -119,23 +110,24 @@ static enum launch_status parse_phdr(struct elf_image *image)
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
vaddr = phdr.p_vaddr;
|
||||
vlimit = phdr.p_vaddr + phdr.p_memsz;
|
||||
if (vaddr & (PAGE_SIZE - 1)) {
|
||||
vaddr &= ~(PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
if (vlimit & (PAGE_SIZE - 1)) {
|
||||
vlimit &= ~(PAGE_SIZE - 1);
|
||||
vlimit += PAGE_SIZE;
|
||||
}
|
||||
|
||||
switch (phdr.p_type) {
|
||||
case PT_DYNAMIC:
|
||||
image->e_dynamic = phdr;
|
||||
break;
|
||||
case PT_LOAD:
|
||||
if (phdr.p_vaddr & (PAGE_SIZE - 1)) {
|
||||
phdr.p_vaddr &= (PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
if (phdr.p_memsz & (PAGE_SIZE - 1)) {
|
||||
phdr.p_memsz &= (PAGE_SIZE - 1);
|
||||
phdr.p_memsz += PAGE_SIZE;
|
||||
}
|
||||
|
||||
image->e_total_size
|
||||
= MAX(image->e_total_size,
|
||||
phdr.p_vaddr + phdr.p_memsz);
|
||||
image->e_total_size = MAX(image->e_total_size, vlimit);
|
||||
break;
|
||||
case PT_INTERP: {
|
||||
size_t r = 0;
|
||||
@@ -153,9 +145,7 @@ static enum launch_status parse_phdr(struct elf_image *image)
|
||||
}
|
||||
|
||||
if (phdr.p_flags & PF_W) {
|
||||
image->e_data_size
|
||||
= MAX(image->e_data_size,
|
||||
phdr.p_vaddr + phdr.p_memsz);
|
||||
image->e_data_size = MAX(image->e_data_size, vlimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +154,10 @@ static enum launch_status parse_phdr(struct elf_image *image)
|
||||
|
||||
static kern_status_t create_exec_regions(struct elf_image *image)
|
||||
{
|
||||
kern_tracef(
|
||||
"launch: executable region size=%zu %zx",
|
||||
image->e_total_size,
|
||||
image->e_total_size);
|
||||
kern_status_t status = KERN_OK;
|
||||
if (image->e_local_space != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_create(
|
||||
@@ -196,7 +190,10 @@ static kern_status_t create_exec_regions(struct elf_image *image)
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
/* TODO cleanup e_local_exec */
|
||||
vm_region_kill(image->e_local_exec);
|
||||
kern_handle_close(image->e_local_exec);
|
||||
image->e_local_exec = KERN_HANDLE_INVALID;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -240,6 +237,7 @@ static enum launch_status map_executable(struct elf_image *image)
|
||||
if (phdr.p_flags & PF_W) {
|
||||
vmo = image->e_data;
|
||||
offset = data_offset;
|
||||
size_t tmp = 0;
|
||||
|
||||
status = vm_object_copy(
|
||||
image->e_data,
|
||||
@@ -247,7 +245,11 @@ static enum launch_status map_executable(struct elf_image *image)
|
||||
image->e_image,
|
||||
phdr.p_offset,
|
||||
phdr.p_filesz,
|
||||
NULL);
|
||||
&tmp);
|
||||
|
||||
if (tmp != phdr.p_filesz) {
|
||||
return LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
@@ -298,8 +300,14 @@ static enum launch_status map_executable(struct elf_image *image)
|
||||
|
||||
static elf_sym_t *get_dynsym(struct elf_image *image, size_t index)
|
||||
{
|
||||
return (elf_sym_t *)(image->e_local_base + image->e_dynsym
|
||||
+ (index * image->e_dynsym_entsize));
|
||||
elf_sym_t *sym = (elf_sym_t *)(image->e_local_base + image->e_dynsym
|
||||
+ (index * image->e_dynsym_entsize));
|
||||
|
||||
if (!sym->st_value) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela)
|
||||
@@ -310,6 +318,10 @@ static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela)
|
||||
switch (type) {
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
sym = get_dynsym(image, ELF64_R_SYM(rela->r_info));
|
||||
if (!sym) {
|
||||
return LAUNCH_ERR_MISSING_SYMBOL;
|
||||
}
|
||||
|
||||
*(uint64_t *)(image->e_local_base + rela->r_offset)
|
||||
= image->e_remote_base + sym->st_value + rela->r_addend;
|
||||
kern_tracef(
|
||||
@@ -352,7 +364,7 @@ static enum launch_status do_rela_list(
|
||||
rela = (elf_rela_t *)((char *)rela + entsize);
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum launch_status do_rel(
|
||||
@@ -383,6 +395,7 @@ static enum launch_status relocate(struct elf_image *image)
|
||||
|
||||
size_t nr_dyn = image->e_dynamic.p_filesz / sizeof *dyn;
|
||||
for (size_t i = 0; i < nr_dyn; i++) {
|
||||
kern_tracef("DYN:%zx", dyn[i].d_tag);
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_SYMTAB:
|
||||
image->e_dynsym = dyn[i].d_un.d_ptr;
|
||||
@@ -478,6 +491,10 @@ void elf_image_init(struct elf_image *out)
|
||||
{
|
||||
memset(out, 0x0, sizeof(*out));
|
||||
|
||||
kern_config_get(
|
||||
KERN_CFG_PAGE_SIZE,
|
||||
&out->e_page_size,
|
||||
sizeof out->e_page_size);
|
||||
out->e_image = KERN_HANDLE_INVALID;
|
||||
out->e_data = KERN_HANDLE_INVALID;
|
||||
out->e_local_space = KERN_HANDLE_INVALID;
|
||||
@@ -542,6 +559,7 @@ void elf_image_cleanup(struct elf_image *image)
|
||||
{
|
||||
vm_region_unmap_relative(image->e_local_exec, 0, image->e_total_size);
|
||||
kern_handle_close(image->e_data);
|
||||
vm_region_kill(image->e_local_exec);
|
||||
kern_handle_close(image->e_local_exec);
|
||||
kern_handle_close(image->e_remote_exec);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,7 @@ struct bootdata;
|
||||
struct bootfs_file;
|
||||
|
||||
struct elf_image {
|
||||
size_t e_page_size;
|
||||
kern_handle_t e_image, e_data;
|
||||
kern_handle_t e_local_space, e_remote_space;
|
||||
kern_handle_t e_local_exec, e_remote_exec;
|
||||
|
||||
@@ -14,6 +14,7 @@ enum launch_status {
|
||||
* version, etc).
|
||||
*/
|
||||
LAUNCH_ERR_UNSUPPORTED_EXECUTABLE,
|
||||
LAUNCH_ERR_MISSING_SYMBOL,
|
||||
/* a particular dependency of the executable could not be resolved. */
|
||||
LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY,
|
||||
LAUNCH_ERR_MEMORY_MAP_FAILED,
|
||||
@@ -28,6 +29,8 @@ enum launch_flags {
|
||||
};
|
||||
|
||||
struct launch_ctx;
|
||||
struct rosetta_bootstrap_handle;
|
||||
struct rosetta_bootstrap_channel;
|
||||
|
||||
typedef enum launch_status (*launch_resolve_library_function)(
|
||||
struct launch_ctx *,
|
||||
@@ -52,6 +55,12 @@ struct launch_parameters {
|
||||
int p_envc;
|
||||
const char **p_envp;
|
||||
|
||||
const struct rosetta_bootstrap_handle *p_handles;
|
||||
size_t p_handle_count;
|
||||
|
||||
const struct rosetta_bootstrap_channel *p_channels;
|
||||
size_t p_channel_count;
|
||||
|
||||
void *p_resolver_arg;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "elf.h"
|
||||
#include "stack.h"
|
||||
|
||||
#include <launch.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/vm.h>
|
||||
#include <rosetta/bootstrap.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -34,6 +36,58 @@ static kern_handle_t get_library(
|
||||
return result;
|
||||
}
|
||||
|
||||
static virt_addr_t write_bootstrap_data(
|
||||
struct stack_writer *stack,
|
||||
const struct launch_parameters *params)
|
||||
{
|
||||
virt_addr_t bs_remote;
|
||||
struct rosetta_bootstrap *bs
|
||||
= stack_writer_put(stack, NULL, sizeof *bs, &bs_remote);
|
||||
|
||||
memset(bs, 0x0, sizeof *bs);
|
||||
|
||||
bs->bs_argc = params->p_argc;
|
||||
bs->bs_envc = params->p_envc;
|
||||
bs->bs_handles_count = params->p_handle_count;
|
||||
bs->bs_channels_count = params->p_channel_count;
|
||||
|
||||
const char **argv, **envp;
|
||||
|
||||
if (bs->bs_argc > 0) {
|
||||
virt_addr_t remote_argv;
|
||||
argv = stack_writer_put(
|
||||
stack,
|
||||
NULL,
|
||||
bs->bs_argc * sizeof(char *),
|
||||
&remote_argv);
|
||||
bs->bs_argv = (const char **)remote_argv;
|
||||
}
|
||||
|
||||
if (bs->bs_envc > 0) {
|
||||
virt_addr_t remote_envp;
|
||||
envp = stack_writer_put(
|
||||
stack,
|
||||
NULL,
|
||||
bs->bs_envc * sizeof(char *),
|
||||
&remote_envp);
|
||||
bs->bs_envp = (const char **)remote_envp;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < params->p_argc; i++) {
|
||||
virt_addr_t arg_ptr;
|
||||
stack_writer_put_string(stack, params->p_argv[i], &arg_ptr);
|
||||
argv[i] = (const char *)arg_ptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < params->p_envc; i++) {
|
||||
virt_addr_t env_ptr;
|
||||
stack_writer_put_string(stack, params->p_envp[i], &env_ptr);
|
||||
envp[i] = (const char *)env_ptr;
|
||||
}
|
||||
|
||||
return bs_remote;
|
||||
}
|
||||
|
||||
enum launch_status launch_ctx_execute(
|
||||
struct launch_ctx *ctx,
|
||||
const struct launch_parameters *params,
|
||||
@@ -102,7 +156,7 @@ enum launch_status launch_ctx_execute(
|
||||
return status;
|
||||
}
|
||||
|
||||
virt_addr_t stack_buf;
|
||||
virt_addr_t remote_stack_buf, local_stack_buf;
|
||||
kstatus = vm_region_map_relative(
|
||||
remote_address_space,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
@@ -110,7 +164,15 @@ enum launch_status launch_ctx_execute(
|
||||
0,
|
||||
STACK_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&stack_buf);
|
||||
&remote_stack_buf);
|
||||
kstatus = vm_region_map_relative(
|
||||
params->p_local_address_space,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
stack_vmo,
|
||||
0,
|
||||
STACK_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&local_stack_buf);
|
||||
kern_handle_close(stack_vmo);
|
||||
|
||||
if (kstatus != KERN_OK) {
|
||||
@@ -120,11 +182,23 @@ enum launch_status launch_ctx_execute(
|
||||
return LAUNCH_ERR_MEMORY_MAP_FAILED;
|
||||
}
|
||||
|
||||
struct stack_writer stack;
|
||||
stack_writer_init(
|
||||
&stack,
|
||||
local_stack_buf + STACK_SIZE,
|
||||
remote_stack_buf + STACK_SIZE);
|
||||
virt_addr_t bsdata = write_bootstrap_data(&stack, params);
|
||||
|
||||
virt_addr_t ip = image.e_hdr.e_entry + image.e_remote_base;
|
||||
virt_addr_t sp = stack_buf + STACK_SIZE;
|
||||
|
||||
kern_handle_t thread;
|
||||
kstatus = task_create_thread(remote_task, ip, sp, NULL, 0, &thread);
|
||||
kstatus = task_create_thread(
|
||||
remote_task,
|
||||
ip,
|
||||
stack.w_remote_sp,
|
||||
&bsdata,
|
||||
1,
|
||||
&thread);
|
||||
if (kstatus != KERN_OK) {
|
||||
elf_image_cleanup(&image);
|
||||
kern_handle_close(remote_address_space);
|
||||
|
||||
59
lib/liblaunch/stack.c
Normal file
59
lib/liblaunch/stack.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "stack.h"
|
||||
|
||||
#include <mango/log.h>
|
||||
#include <string.h>
|
||||
|
||||
void stack_writer_init(
|
||||
struct stack_writer *w,
|
||||
virt_addr_t local_sp,
|
||||
virt_addr_t remote_sp)
|
||||
{
|
||||
memset(w, 0x0, sizeof *w);
|
||||
|
||||
w->w_local_sp = local_sp;
|
||||
w->w_remote_sp = remote_sp;
|
||||
}
|
||||
|
||||
void *stack_writer_put_string(
|
||||
struct stack_writer *w,
|
||||
const char *s,
|
||||
virt_addr_t *out_remote)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
|
||||
w->w_local_sp -= (len + 1);
|
||||
w->w_remote_sp -= (len + 1);
|
||||
|
||||
char *local_ptr = (char *)w->w_local_sp;
|
||||
virt_addr_t remote_ptr = w->w_remote_sp;
|
||||
|
||||
memcpy(local_ptr, s, len);
|
||||
local_ptr[len] = '\0';
|
||||
|
||||
if (out_remote) {
|
||||
*out_remote = remote_ptr;
|
||||
}
|
||||
|
||||
return local_ptr;
|
||||
}
|
||||
|
||||
void *stack_writer_put(
|
||||
struct stack_writer *w,
|
||||
const void *p,
|
||||
size_t len,
|
||||
virt_addr_t *out_remote)
|
||||
{
|
||||
w->w_local_sp -= len;
|
||||
w->w_remote_sp -= len;
|
||||
|
||||
void *local_ptr = (char *)w->w_local_sp;
|
||||
virt_addr_t remote_ptr = w->w_remote_sp;
|
||||
|
||||
memset(local_ptr, 0x0, len);
|
||||
|
||||
if (out_remote) {
|
||||
*out_remote = remote_ptr;
|
||||
}
|
||||
|
||||
return local_ptr;
|
||||
}
|
||||
26
lib/liblaunch/stack.h
Normal file
26
lib/liblaunch/stack.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef LIBLAUNCH_STACK_H_
|
||||
#define LIBLAUNCH_STACK_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
struct stack_writer {
|
||||
virt_addr_t w_local_sp;
|
||||
virt_addr_t w_remote_sp;
|
||||
};
|
||||
|
||||
extern void stack_writer_init(
|
||||
struct stack_writer *w,
|
||||
virt_addr_t local_sp,
|
||||
virt_addr_t remote_sp);
|
||||
|
||||
extern void *stack_writer_put_string(
|
||||
struct stack_writer *w,
|
||||
const char *s,
|
||||
virt_addr_t *out_remote);
|
||||
extern void *stack_writer_put(
|
||||
struct stack_writer *w,
|
||||
const void *p,
|
||||
size_t len,
|
||||
virt_addr_t *out_remote);
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
file(GLOB sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
file(GLOB headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/rosetta/msg/fs.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME libmsg-fs STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libmsg-fs
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libmsg-fs libmsg libmango)
|
||||
@@ -1,3 +0,0 @@
|
||||
void msg_fs_tmp(void)
|
||||
{
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef ROSETTA_MSG_FS_H_
|
||||
#define ROSETTA_MSG_FS_H_
|
||||
|
||||
#include <rosetta/msg.h>
|
||||
|
||||
/* rosetta.msg.fs protocol ID */
|
||||
#define ROSETTA_MSG_FS 0x01409DD2U
|
||||
|
||||
/* rosetta.msg.fs.open message ID */
|
||||
#define ROSETTA_MSG_FS_OPEN 0x7D837778U
|
||||
|
||||
struct rosetta_msg_fs_open {
|
||||
struct rosetta_msg msg_base;
|
||||
union {
|
||||
struct {
|
||||
uint16_t o_path;
|
||||
uint16_t o_flags;
|
||||
} msg_request;
|
||||
|
||||
struct {
|
||||
int o_err;
|
||||
uint16_t o_fd_index;
|
||||
} msg_response;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
file(GLOB sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
file(GLOB headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/rosetta/msg/ld.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME libmsg-ld STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libmsg-ld
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libmsg-ld libmsg libmango)
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef ROSETTA_MSG_LD_H_
|
||||
#define ROSETTA_MSG_LD_H_
|
||||
|
||||
#include <rosetta/msg.h>
|
||||
|
||||
/* rosetta.msg.ld protocol ID */
|
||||
#define ROSETTA_MSG_LD 0x5563D896U
|
||||
|
||||
/* rosetta.msg.ld.get-object message ID */
|
||||
#define ROSETTA_MSG_LD_GET_OBJECT 1
|
||||
|
||||
struct rosetta_msg_ld_get_object {
|
||||
struct rosetta_msg msg_base;
|
||||
union {
|
||||
struct {
|
||||
uint16_t go_object_name;
|
||||
} msg_request;
|
||||
|
||||
struct {
|
||||
int go_err;
|
||||
uint16_t go_object_index;
|
||||
} msg_response;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,3 +0,0 @@
|
||||
void msg_ld_tmp(void)
|
||||
{
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
file(GLOB sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
file(GLOB headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/rosetta/msg.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME libmsg STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libmsg
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libmsg libmango)
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef ROSETTA_MSG_H_
|
||||
#define ROSETTA_MSG_H_
|
||||
|
||||
#define ROSETTA_MSG_MAGIC 0x9AB07D10U
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct rosetta_msg {
|
||||
uint32_t msg_magic;
|
||||
uint32_t msg_protocol;
|
||||
uint16_t msg_id;
|
||||
uint16_t msg_reserved;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,3 +0,0 @@
|
||||
void msg_tmp(void)
|
||||
{
|
||||
}
|
||||
15
lib/librosetta/CMakeLists.txt
Normal file
15
lib/librosetta/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
file(GLOB sources *.c)
|
||||
file(GLOB headers include/rosetta/*.h)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME librosetta STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME librosetta
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(librosetta libmango)
|
||||
6
lib/librosetta/bootstrap.c
Normal file
6
lib/librosetta/bootstrap.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <rosetta/bootstrap.h>
|
||||
|
||||
const struct rosetta_bootstrap_channel *rosetta_bootstrap_get_channel(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
45
lib/librosetta/include/rosetta/bootstrap.h
Normal file
45
lib/librosetta/include/rosetta/bootstrap.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef ROSETTA_BOOTSTRAP_H_
|
||||
#define ROSETTA_BOOTSTRAP_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
enum rosetta_bootstrap_handle_type {
|
||||
RSBS_HANDLE_NONE = 0,
|
||||
RSBS_HANDLE_TASK,
|
||||
RSBS_HANDLE_ADDRESS_SPACE,
|
||||
};
|
||||
|
||||
enum rosetta_bootstrap_channel_type {
|
||||
RSBS_CHANNEL_NONE,
|
||||
RSBS_CHANNEL_SYSTEM,
|
||||
};
|
||||
|
||||
struct rosetta_bootstrap_handle {
|
||||
enum rosetta_bootstrap_handle_type h_type;
|
||||
kern_handle_t h_value;
|
||||
};
|
||||
|
||||
struct rosetta_bootstrap_channel {
|
||||
enum rosetta_bootstrap_channel_type c_type;
|
||||
tid_t c_tid;
|
||||
unsigned int c_cid;
|
||||
};
|
||||
|
||||
struct rosetta_bootstrap {
|
||||
int bs_argc;
|
||||
const char **bs_argv;
|
||||
|
||||
int bs_envc;
|
||||
const char **bs_envp;
|
||||
|
||||
const struct rosetta_bootstrap_handle *bs_handles;
|
||||
size_t bs_handles_count;
|
||||
|
||||
const struct rosetta_bootstrap_channel *bs_channels;
|
||||
size_t bs_channels_count;
|
||||
};
|
||||
|
||||
extern const struct rosetta_bootstrap_channel *rosetta_bootstrap_get_channel(
|
||||
void);
|
||||
|
||||
#endif
|
||||
28
lib/libxpc/CMakeLists.txt
Normal file
28
lib/libxpc/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
file(GLOB sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
file(GLOB headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/xpc/*.h)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME libxpc SHARED STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libxpc
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
sysroot_add_library(
|
||||
NAME libxpc-static
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libxpc libmango libc)
|
||||
target_link_libraries(libxpc-static libmango libc-core)
|
||||
|
||||
#set_target_properties(libxpc-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE)
|
||||
59
lib/libxpc/buffer.c
Normal file
59
lib/libxpc/buffer.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <mango/status.h>
|
||||
#include <xpc/buffer.h>
|
||||
#include <xpc/msg.h>
|
||||
|
||||
kern_status_t xpc_buffer_read(
|
||||
const xpc_buffer_t *buf,
|
||||
void *out,
|
||||
size_t max,
|
||||
size_t *nr_read)
|
||||
{
|
||||
if ((buf->buf_flags & (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE))
|
||||
!= (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE)) {
|
||||
return KERN_BAD_STATE;
|
||||
}
|
||||
|
||||
size_t to_read = max;
|
||||
if (to_read > buf->buf_len) {
|
||||
to_read = buf->buf_len;
|
||||
}
|
||||
|
||||
kern_status_t status
|
||||
= xpc_msg_read(buf->buf_origin, buf->buf_offset, out, to_read);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
*nr_read = to_read;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t xpc_buffer_write(
|
||||
xpc_buffer_t *buf,
|
||||
const void *in,
|
||||
size_t len,
|
||||
size_t *nr_written)
|
||||
{
|
||||
if ((buf->buf_flags & (XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE))
|
||||
!= (XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE)) {
|
||||
return KERN_BAD_STATE;
|
||||
}
|
||||
|
||||
size_t to_write = len;
|
||||
if (to_write > buf->buf_max) {
|
||||
to_write = buf->buf_max;
|
||||
}
|
||||
|
||||
kern_status_t status
|
||||
= xpc_msg_write(buf->buf_origin, buf->buf_offset, in, to_write);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
*nr_written = to_write;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
86
lib/libxpc/include/xpc/buffer.h
Normal file
86
lib/libxpc/include/xpc/buffer.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef XPC_BUFFER_H_
|
||||
#define XPC_BUFFER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <xpc/status.h>
|
||||
|
||||
#define XPC_BUFFER_IN(msg, offset, size) \
|
||||
{ \
|
||||
.buf_flags = XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE, \
|
||||
.buf_origin = (msg), \
|
||||
.buf_offset = (offset), \
|
||||
.buf_len = (size), \
|
||||
}
|
||||
#define XPC_BUFFER_OUT(msg, offset, size) \
|
||||
{ \
|
||||
.buf_flags = XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE, \
|
||||
.buf_origin = (msg), \
|
||||
.buf_offset = (offset), \
|
||||
.buf_len = (size), \
|
||||
}
|
||||
|
||||
struct xpc_msg;
|
||||
|
||||
typedef enum xpc_buffer_flags {
|
||||
/* the buffer can be read from */
|
||||
XPC_BUFFER_F_IN = 0x01u,
|
||||
/* the buffer can be written to */
|
||||
XPC_BUFFER_F_OUT = 0x02u,
|
||||
/* the buffer is backed by a buffer located in another address space.
|
||||
* the buffer can only be accessed via xpc_buffer_read and/or
|
||||
* xpc_buffer_write */
|
||||
XPC_BUFFER_F_REMOTE = 0x04u,
|
||||
/* free the buffer backing this buffer when the buffer is discarded.
|
||||
* this is only used for out-buffers. the buffer must have been
|
||||
* allocated using xpc_context_alloc, as it will be freed via a call
|
||||
* to xpc_context_free */
|
||||
XPC_BUFFER_F_FREE_ON_DISCARD = 0x08u,
|
||||
} xpc_buffer_flags_t;
|
||||
|
||||
typedef struct xpc_buffer {
|
||||
xpc_buffer_flags_t buf_flags;
|
||||
union {
|
||||
/* fields that are only valid if F_OUT is set */
|
||||
struct {
|
||||
/* only valid if F_OUT is set. specifies the maximum
|
||||
* number of chars that can be written to buf_buf,
|
||||
* including the null terminator. */
|
||||
size_t buf_max;
|
||||
/* only valid if F_OUT is set.
|
||||
* if F_FREE_ON_DISCARD is set, must be either NULL or
|
||||
* allocated via xpc_context_alloc */
|
||||
const char *buf_ptr;
|
||||
};
|
||||
|
||||
/* fields that are only valid if F_IN is set */
|
||||
struct {
|
||||
/* only valid if F_IN is set. offset of the buffer data
|
||||
* within the associated message. used when reading
|
||||
* buffer data from a message. */
|
||||
size_t buf_offset;
|
||||
};
|
||||
};
|
||||
|
||||
/* only valid if F_REMOTE is set.
|
||||
* used to read/write buffer data from/to the sender's address
|
||||
* space. */
|
||||
const struct xpc_msg *buf_origin;
|
||||
|
||||
/* valid for both F_IN and F_OUT buffers.
|
||||
* F_IN: specifies the length of the incoming buffer data.
|
||||
* F_OUT: specifies how many bytes from buf_ptr to send. */
|
||||
size_t buf_len;
|
||||
} xpc_buffer_t;
|
||||
|
||||
extern xpc_status_t xpc_buffer_read(
|
||||
const xpc_buffer_t *s,
|
||||
void *out,
|
||||
size_t max,
|
||||
size_t *nr_read);
|
||||
extern xpc_status_t xpc_buffer_write(
|
||||
xpc_buffer_t *s,
|
||||
const void *in,
|
||||
size_t len,
|
||||
size_t *nr_written);
|
||||
|
||||
#endif
|
||||
8
lib/libxpc/include/xpc/context.h
Normal file
8
lib/libxpc/include/xpc/context.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef XPC_CONTEXT_H_
|
||||
#define XPC_CONTEXT_H_
|
||||
|
||||
typedef struct xpc_context {
|
||||
|
||||
} xpc_context_t;
|
||||
|
||||
#endif
|
||||
13
lib/libxpc/include/xpc/endpoint.h
Normal file
13
lib/libxpc/include/xpc/endpoint.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef XPC_ENDPOINT_H_
|
||||
#define XPC_ENDPOINT_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
typedef struct xpc_endpoint {
|
||||
kern_handle_t e_channel;
|
||||
tid_t e_task;
|
||||
koid_t e_port;
|
||||
msgid_t e_msg;
|
||||
} xpc_endpoint_t;
|
||||
|
||||
#endif
|
||||
53
lib/libxpc/include/xpc/msg.h
Normal file
53
lib/libxpc/include/xpc/msg.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef XPC_MSG_H_
|
||||
#define XPC_MSG_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <xpc/endpoint.h>
|
||||
|
||||
#define XPC_MSG_MAGIC 0x5850434D
|
||||
|
||||
typedef struct xpc_msg_header {
|
||||
uint32_t hdr_magic;
|
||||
uint32_t hdr_interface;
|
||||
uint16_t hdr_func;
|
||||
uint16_t hdr_status;
|
||||
} xpc_msg_header_t;
|
||||
|
||||
typedef struct xpc_msg {
|
||||
xpc_endpoint_t msg_sender;
|
||||
xpc_msg_header_t msg_header;
|
||||
size_t msg_handles_count;
|
||||
|
||||
kern_msg_handle_t msg_handles[KERN_MSG_MAX_HANDLES];
|
||||
} xpc_msg_t;
|
||||
|
||||
extern void xpc_msg_header_init(
|
||||
xpc_msg_header_t *msg,
|
||||
unsigned long interface,
|
||||
unsigned short func);
|
||||
extern bool xpc_msg_header_validate(const xpc_msg_header_t *msg);
|
||||
extern kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out);
|
||||
extern kern_status_t xpc_msg_read(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
void *p,
|
||||
size_t count);
|
||||
extern kern_status_t xpc_msg_write(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
const void *p,
|
||||
size_t count);
|
||||
|
||||
extern kern_status_t xpc_msg_reply(
|
||||
const xpc_msg_t *msg,
|
||||
kern_iovec_t *iov,
|
||||
size_t iov_count,
|
||||
kern_msg_handle_t *handles,
|
||||
size_t handle_count);
|
||||
extern kern_status_t xpc_msg_reply_error(
|
||||
const xpc_msg_t *msg,
|
||||
unsigned short code);
|
||||
|
||||
#endif
|
||||
12
lib/libxpc/include/xpc/status.h
Normal file
12
lib/libxpc/include/xpc/status.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef XPC_STATUS_H_
|
||||
#define XPC_STATUS_H_
|
||||
|
||||
typedef enum xpc_status {
|
||||
XPC_SUCCESS = 0,
|
||||
XPC_ERR_BAD_STATE,
|
||||
XPC_ERR_INVALID_ARGUMENT,
|
||||
XPC_ERR_NO_MEMORY,
|
||||
XPC_ERR_MEMORY_FAULT,
|
||||
} xpc_status_t;
|
||||
|
||||
#endif
|
||||
85
lib/libxpc/include/xpc/string.h
Normal file
85
lib/libxpc/include/xpc/string.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef XPC_STRING_H_
|
||||
#define XPC_STRING_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <xpc/status.h>
|
||||
|
||||
#define XPC_STRING_NPOS ((size_t)-1)
|
||||
|
||||
#define XPC_STRING_IN(msg, offset, size) \
|
||||
{ \
|
||||
.s_flags = XPC_STRING_F_IN | XPC_STRING_F_REMOTE, \
|
||||
.s_origin = (msg), \
|
||||
.s_offset = (offset), \
|
||||
.s_len = (size), \
|
||||
}
|
||||
#define XPC_STRING_OUT(msg, offset, size) \
|
||||
{ \
|
||||
.s_flags = XPC_STRING_F_OUT | XPC_STRING_F_REMOTE, \
|
||||
.s_origin = (msg), \
|
||||
.s_offset = (offset), \
|
||||
.s_len = (size), \
|
||||
}
|
||||
|
||||
struct xpc_msg;
|
||||
|
||||
typedef enum xpc_string_flags {
|
||||
/* the string can be read from */
|
||||
XPC_STRING_F_IN = 0x01u,
|
||||
/* the string can be written to */
|
||||
XPC_STRING_F_OUT = 0x02u,
|
||||
/* the string is backed by a buffer located in another address space.
|
||||
* the string can only be accessed via xpc_string_read and/or
|
||||
* xpc_string_write */
|
||||
XPC_STRING_F_REMOTE = 0x04u,
|
||||
/* free the buffer backing this string when the string is discarded.
|
||||
* this is only used for out-strings. the buffer must have been
|
||||
* allocated using xpc_context_alloc, as it will be freed via a call
|
||||
* to xpc_context_free */
|
||||
XPC_STRING_F_FREE_ON_DISCARD = 0x08u,
|
||||
} xpc_string_flags_t;
|
||||
|
||||
typedef struct xpc_string {
|
||||
xpc_string_flags_t s_flags;
|
||||
union {
|
||||
struct {
|
||||
/* only valid if F_OUT is set. specifies the maximum
|
||||
* number of chars that can be written to s_buf,
|
||||
* including the null terminator. */
|
||||
size_t s_max;
|
||||
/* only valid if F_OUT is set.
|
||||
* if F_FREE_ON_DISCARD is set, must be either NULL or
|
||||
* allocated via xpc_context_alloc */
|
||||
const char *s_buf;
|
||||
};
|
||||
|
||||
struct {
|
||||
/* only valid if F_IN is set. offset of the string data
|
||||
* within the associated message. used when reading
|
||||
* string data from a message. */
|
||||
size_t s_offset;
|
||||
};
|
||||
};
|
||||
|
||||
/* only valid if F_REMOTE is set.
|
||||
* used to read/write string data from/to the sender's address space. */
|
||||
const struct xpc_msg *s_origin;
|
||||
|
||||
/* valid for both F_IN and F_OUT strings.
|
||||
* F_IN: specifies the length of the incoming string data.
|
||||
* F_OUT: specifies how many characters from s_buf to send. */
|
||||
size_t s_len;
|
||||
} xpc_string_t;
|
||||
|
||||
extern xpc_status_t xpc_string_read(
|
||||
const xpc_string_t *s,
|
||||
char *out,
|
||||
size_t max,
|
||||
size_t *nr_read);
|
||||
extern xpc_status_t xpc_string_write(
|
||||
xpc_string_t *s,
|
||||
const char *in,
|
||||
size_t len,
|
||||
size_t *nr_written);
|
||||
|
||||
#endif
|
||||
112
lib/libxpc/msg.c
Normal file
112
lib/libxpc/msg.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <mango/msg.h>
|
||||
#include <string.h>
|
||||
#include <xpc/msg.h>
|
||||
|
||||
void xpc_msg_header_init(
|
||||
xpc_msg_header_t *msg,
|
||||
unsigned long interface,
|
||||
unsigned short func)
|
||||
{
|
||||
memset(msg, 0x0, sizeof *msg);
|
||||
|
||||
msg->hdr_magic = XPC_MSG_MAGIC;
|
||||
msg->hdr_interface = interface;
|
||||
msg->hdr_func = func;
|
||||
msg->hdr_status = 0;
|
||||
}
|
||||
|
||||
bool xpc_msg_header_validate(const xpc_msg_header_t *msg)
|
||||
{
|
||||
return msg->hdr_magic == XPC_MSG_MAGIC;
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out)
|
||||
{
|
||||
kern_iovec_t iov = IOVEC(&out->msg_header, sizeof out->msg_header);
|
||||
kern_msg_t msg = {
|
||||
.msg_data = &iov,
|
||||
.msg_data_count = 1,
|
||||
.msg_handles = out->msg_handles,
|
||||
.msg_handles_count = KERN_MSG_MAX_HANDLES,
|
||||
};
|
||||
kern_status_t status = msg_recv(channel, &msg);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!xpc_msg_header_validate(&out->msg_header)) {
|
||||
return KERN_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
out->msg_sender.e_channel = channel;
|
||||
out->msg_sender.e_task = msg.msg_sender;
|
||||
out->msg_sender.e_port = msg.msg_endpoint;
|
||||
out->msg_sender.e_msg = msg.msg_id;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_read(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
void *p,
|
||||
size_t count)
|
||||
{
|
||||
kern_iovec_t iov = IOVEC(p, count);
|
||||
size_t r = 0;
|
||||
return msg_read(
|
||||
msg->msg_sender.e_channel,
|
||||
msg->msg_sender.e_msg,
|
||||
offset,
|
||||
&iov,
|
||||
1,
|
||||
&r);
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_write(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
const void *p,
|
||||
size_t count)
|
||||
{
|
||||
kern_iovec_t iov = IOVEC(p, count);
|
||||
size_t w = 0;
|
||||
return msg_write(
|
||||
msg->msg_sender.e_channel,
|
||||
msg->msg_sender.e_msg,
|
||||
offset,
|
||||
&iov,
|
||||
1,
|
||||
&w);
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_reply(
|
||||
const xpc_msg_t *msg,
|
||||
kern_iovec_t *iov,
|
||||
size_t iov_count,
|
||||
kern_msg_handle_t *handles,
|
||||
size_t handle_count)
|
||||
{
|
||||
kern_msg_t reply = MSG(iov, iov_count, handles, handle_count);
|
||||
return msg_reply(
|
||||
msg->msg_sender.e_channel,
|
||||
msg->msg_sender.e_msg,
|
||||
&reply);
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_reply_error(const xpc_msg_t *msg, unsigned short code)
|
||||
{
|
||||
xpc_msg_header_t reply_data = {
|
||||
.hdr_magic = XPC_MSG_MAGIC,
|
||||
.hdr_interface = msg->msg_header.hdr_interface,
|
||||
.hdr_func = msg->msg_header.hdr_func,
|
||||
.hdr_status = code,
|
||||
};
|
||||
|
||||
kern_iovec_t iov = IOVEC(&reply_data, sizeof reply_data);
|
||||
kern_msg_t reply = MSG(&iov, 1, NULL, 0);
|
||||
return msg_reply(
|
||||
msg->msg_sender.e_channel,
|
||||
msg->msg_sender.e_msg,
|
||||
&reply);
|
||||
}
|
||||
60
lib/libxpc/string.c
Normal file
60
lib/libxpc/string.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <mango/status.h>
|
||||
#include <xpc/msg.h>
|
||||
#include <xpc/string.h>
|
||||
|
||||
xpc_status_t xpc_string_read(
|
||||
const xpc_string_t *s,
|
||||
char *out,
|
||||
size_t max,
|
||||
size_t *nr_read)
|
||||
{
|
||||
if ((s->s_flags & (XPC_STRING_F_IN | XPC_STRING_F_REMOTE))
|
||||
!= (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) {
|
||||
return KERN_BAD_STATE;
|
||||
}
|
||||
|
||||
size_t to_read = max - 1;
|
||||
if (to_read > s->s_len) {
|
||||
to_read = s->s_len;
|
||||
}
|
||||
|
||||
kern_status_t status
|
||||
= xpc_msg_read(s->s_origin, s->s_offset, out, to_read);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
out[to_read] = '\0';
|
||||
/* TODO */
|
||||
*nr_read = to_read;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
xpc_status_t xpc_string_write(
|
||||
xpc_string_t *s,
|
||||
const char *in,
|
||||
size_t len,
|
||||
size_t *nr_written)
|
||||
{
|
||||
if ((s->s_flags & (XPC_STRING_F_OUT | XPC_STRING_F_REMOTE))
|
||||
!= (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) {
|
||||
return KERN_BAD_STATE;
|
||||
}
|
||||
|
||||
size_t to_write = len;
|
||||
if (to_write > s->s_max - 1) {
|
||||
to_write = s->s_max - 1;
|
||||
}
|
||||
|
||||
kern_status_t status
|
||||
= xpc_msg_write(s->s_origin, s->s_offset, in, to_write);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
*nr_written = to_write;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef STDIO_H_
|
||||
#define STDIO_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int snprintf(char *buffer, size_t count, const char *format, ...);
|
||||
extern int vsnprintf(
|
||||
char *buffer,
|
||||
size_t count,
|
||||
const char *format,
|
||||
va_list va);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef STRING_H_
|
||||
#define STRING_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern size_t strlen(const char *s);
|
||||
|
||||
extern int strcmp(const char *s1, const char *s2);
|
||||
extern int strncmp(const char *s1, const char *s2, unsigned long n);
|
||||
|
||||
extern void *memset(void *str, int c, size_t n);
|
||||
extern void *memcpy(void *dst, const void *src, size_t len);
|
||||
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef UNISTD_H_
|
||||
#define UNISTD_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern void *sbrk(intptr_t increment);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user