diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..15d4eaa --- /dev/null +++ b/.clang-format @@ -0,0 +1,561 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 4. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +#AlignEscapedNewlines: Left # Unknown to clang-format-4.0 +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + #AfterExternBlock: false # Unknown to clang-format-5.0 + BeforeCatch: false + BeforeElse: false + IndentBraces: false + #SplitEmptyFunction: true # Unknown to clang-format-4.0 + #SplitEmptyRecord: true # Unknown to clang-format-4.0 + #SplitEmptyNamespace: true # Unknown to clang-format-4.0 +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +#CompactNamespaces: false # Unknown to clang-format-4.0 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +#FixNamespaceComments: false # Unknown to clang-format-4.0 + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +ForEachMacros: + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - '__ata_qc_for_each' + - 'ata_qc_for_each' + - 'ata_qc_for_each_raw' + - 'ata_qc_for_each_with_internal' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - '__bio_for_each_bvec' + - 'bio_for_each_bvec' + - 'bio_for_each_bvec_all' + - 'bio_for_each_integrity_vec' + - '__bio_for_each_segment' + - 'bio_for_each_segment' + - 'bio_for_each_segment_all' + - 'bio_list_for_each' + - 'bip_for_each_vec' + - 'bitmap_for_each_clear_region' + - 'bitmap_for_each_set_region' + - 'blkg_for_each_descendant_post' + - 'blkg_for_each_descendant_pre' + - 'blk_queue_for_each_rl' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - 'bpf_for_each_spilled_reg' + - 'btree_for_each_safe128' + - 'btree_for_each_safe32' + - 'btree_for_each_safe64' + - 'btree_for_each_safel' + - 'card_for_each_dev' + - 'cgroup_taskset_for_each' + - 'cgroup_taskset_for_each_leader' + - 'cpufreq_for_each_entry' + - 'cpufreq_for_each_entry_idx' + - 'cpufreq_for_each_valid_entry' + - 'cpufreq_for_each_valid_entry_idx' + - 'css_for_each_child' + - 'css_for_each_descendant_post' + - 'css_for_each_descendant_pre' + - 'device_for_each_child_node' + - 'displayid_iter_for_each' + - 'dma_fence_chain_for_each' + - 'do_for_each_ftrace_op' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_atomic_for_each_plane_damage' + - 'drm_client_for_each_connector_iter' + - 'drm_client_for_each_modeset' + - 'drm_connector_for_each_possible_encoder' + - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_crtc_reverse' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_for_each_privobj' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'flow_action_for_each' + - 'for_each_acpi_dev_match' + - 'for_each_active_dev_scope' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_aggr_pgid' + - 'for_each_available_child_of_node' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_bvec' + - 'for_each_card_auxs' + - 'for_each_card_auxs_safe' + - 'for_each_card_components' + - 'for_each_card_dapms' + - 'for_each_card_pre_auxs' + - 'for_each_card_prelinks' + - 'for_each_card_rtds' + - 'for_each_card_rtds_safe' + - 'for_each_card_widgets' + - 'for_each_card_widgets_safe' + - 'for_each_cgroup_storage_type' + - 'for_each_child_of_node' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_cmsghdr' + - 'for_each_compatible_node' + - 'for_each_component_dais' + - 'for_each_component_dais_safe' + - 'for_each_comp_order' + - 'for_each_console' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_not' + - 'for_each_cpu_wrap' + - 'for_each_dapm_widgets' + - 'for_each_dev_addr' + - 'for_each_dev_scope' + - 'for_each_dma_cap_mask' + - 'for_each_dpcm_be' + - 'for_each_dpcm_be_rollback' + - 'for_each_dpcm_be_safe' + - 'for_each_dpcm_fe' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_dtpm_table' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_element' + - 'for_each_element_extid' + - 'for_each_element_id' + - 'for_each_endpoint_of_node' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - 'for_each_free_mem_pfn_range_in_zone' + - 'for_each_free_mem_pfn_range_in_zone_from' + - 'for_each_free_mem_range' + - 'for_each_free_mem_range_reverse' + - 'for_each_func_rsrc' + - 'for_each_hstate' + - 'for_each_if' + - 'for_each_iommu' + - 'for_each_ip_tunnel_rcu' + - 'for_each_irq_nr' + - 'for_each_link_codecs' + - 'for_each_link_cpus' + - 'for_each_link_platforms' + - 'for_each_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_member' + - 'for_each_memcg_cache_index' + - 'for_each_mem_pfn_range' + - '__for_each_mem_range' + - 'for_each_mem_range' + - '__for_each_mem_range_rev' + - 'for_each_mem_range_rev' + - 'for_each_mem_region' + - 'for_each_migratetype_order' + - 'for_each_msi_entry' + - 'for_each_msi_entry_safe' + - 'for_each_msi_vector' + - 'for_each_net' + - 'for_each_net_continue_reverse' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - 'for_each_netdev_continue_reverse' + - 'for_each_netdev_feature' + - 'for_each_netdev_in_bond_rcu' + - 'for_each_netdev_rcu' + - 'for_each_netdev_reverse' + - 'for_each_netdev_safe' + - 'for_each_net_rcu' + - 'for_each_new_connector_in_state' + - 'for_each_new_crtc_in_state' + - 'for_each_new_mst_mgr_in_state' + - 'for_each_new_plane_in_state' + - 'for_each_new_private_obj_in_state' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_nonreserved_multicast_dest_pgid' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_cpu_node' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_old_mst_mgr_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_mst_mgr_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_plane_in_state_reverse' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pci_msi_entry' + - 'for_each_pcm_streams' + - 'for_each_physmem_range' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_present_cpu' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_prop_codec_conf' + - 'for_each_prop_dai_codec' + - 'for_each_prop_dai_cpu' + - 'for_each_prop_dlc_codecs' + - 'for_each_prop_dlc_cpus' + - 'for_each_prop_dlc_platforms' + - 'for_each_property_of_node' + - 'for_each_registered_fb' + - 'for_each_requested_gpio' + - 'for_each_requested_gpio_in_range' + - 'for_each_reserved_mem_range' + - 'for_each_reserved_mem_region' + - 'for_each_rtd_codec_dais' + - 'for_each_rtd_components' + - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_dais' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_set_clump8' + - 'for_each_sg' + - 'for_each_sg_dma_page' + - 'for_each_sg_page' + - 'for_each_sgtable_dma_page' + - 'for_each_sgtable_dma_sg' + - 'for_each_sgtable_page' + - 'for_each_sgtable_sg' + - 'for_each_sibling_event' + - 'for_each_subelement' + - 'for_each_subelement_extid' + - 'for_each_subelement_id' + - '__for_each_thread' + - 'for_each_thread' + - 'for_each_unicast_dest_pgid' + - 'for_each_vsi' + - 'for_each_wakeup_source' + - 'for_each_zone' + - 'for_each_zone_zonelist' + - 'for_each_zone_zonelist_nodemask' + - 'fwnode_for_each_available_child_node' + - 'fwnode_for_each_child_node' + - 'fwnode_graph_for_each_endpoint' + - 'gadget_for_each_ep' + - 'genradix_for_each' + - 'genradix_for_each_from' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hctx_for_each_ctx' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - 'hlist_for_each_entry_srcu' + - '__hlist_for_each_rcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - 'i3c_bus_for_each_i2cdev' + - 'i3c_bus_for_each_i3cdev' + - 'ide_host_for_each_port' + - 'ide_port_for_each_dev' + - 'ide_port_for_each_present_dev' + - 'idr_for_each_entry' + - 'idr_for_each_entry_continue' + - 'idr_for_each_entry_continue_ul' + - 'idr_for_each_entry_ul' + - 'in_dev_for_each_ifa_rcu' + - 'in_dev_for_each_ifa_rtnl' + - 'inet_bind_bucket_for_each' + - 'inet_lhash2_for_each_icsk_rcu' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_func_safe' + - 'klp_for_each_func_static' + - 'klp_for_each_object' + - 'klp_for_each_object_safe' + - 'klp_for_each_object_static' + - 'kunit_suite_for_each_test_case' + - 'kvm_for_each_memslot' + - 'kvm_for_each_vcpu' + - 'list_for_each' + - 'list_for_each_codec' + - 'list_for_each_codec_safe' + - 'list_for_each_continue' + - 'list_for_each_entry' + - 'list_for_each_entry_continue' + - 'list_for_each_entry_continue_rcu' + - 'list_for_each_entry_continue_reverse' + - 'list_for_each_entry_from' + - 'list_for_each_entry_from_rcu' + - 'list_for_each_entry_from_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_entry_srcu' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'mci_for_each_dimm' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'nanddev_io_for_each_page' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'pcl_for_each_chunk' + - 'pcl_for_each_segment' + - 'pcm_for_each_format' + - 'ping_portaddr_for_each_entry' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rb_for_each' + - 'rbtree_postorder_for_each_entry_safe' + - 'rdma_for_each_block' + - 'rdma_for_each_port' + - 'rdma_umem_for_each_dma_block' + - 'resource_list_for_each_entry' + - 'resource_list_for_each_entry_safe' + - 'rhl_for_each_entry_rcu' + - 'rhl_for_each_rcu' + - 'rht_for_each' + - 'rht_for_each_entry' + - 'rht_for_each_entry_from' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_from' + - 'rht_for_each_entry_safe' + - 'rht_for_each_from' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_from' + - '__rq_for_each_bio' + - 'rq_for_each_bvec' + - 'rq_for_each_segment' + - 'scsi_for_each_prot_sg' + - 'scsi_for_each_sg' + - 'sctp_for_each_hentry' + - 'sctp_skb_for_each' + - 'shdma_for_each_chan' + - '__shost_for_each_device' + - 'shost_for_each_device' + - 'sk_for_each' + - 'sk_for_each_bound' + - 'sk_for_each_entry_offset_rcu' + - 'sk_for_each_from' + - 'sk_for_each_rcu' + - 'sk_for_each_safe' + - 'sk_nulls_for_each' + - 'sk_nulls_for_each_from' + - 'sk_nulls_for_each_rcu' + - 'snd_array_for_each' + - 'snd_pcm_group_for_each_entry' + - 'snd_soc_dapm_widget_for_each_path' + - 'snd_soc_dapm_widget_for_each_path_safe' + - 'snd_soc_dapm_widget_for_each_sink_path' + - 'snd_soc_dapm_widget_for_each_source_path' + - 'tb_property_for_each' + - 'tcf_exts_for_each_action' + - 'udp_portaddr_for_each_entry' + - 'udp_portaddr_for_each_entry_rcu' + - 'usb_hub_for_each_child' + - 'v4l2_device_for_each_subdev' + - 'v4l2_m2m_for_each_dst_buf' + - 'v4l2_m2m_for_each_dst_buf_safe' + - 'v4l2_m2m_for_each_src_buf' + - 'v4l2_m2m_for_each_src_buf_safe' + - 'virtio_device_for_each_vq' + - 'while_for_each_ftrace_op' + - 'xa_for_each' + - 'xa_for_each_marked' + - 'xa_for_each_range' + - 'xa_for_each_start' + - 'xas_for_each' + - 'xas_for_each_conflict' + - 'xas_for_each_marked' + - 'xbc_array_for_each_value' + - 'xbc_for_each_key_value' + - 'xbc_node_for_each_array_value' + - 'xbc_node_for_each_child' + - 'xbc_node_for_each_key_value' + - 'zorro_for_each_dev' + +#IncludeBlocks: Preserve # Unknown to clang-format-5.0 +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +#IndentPPDirectives: None # Unknown to clang-format-5.0 +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +#SortUsingDeclarations: false # Unknown to clang-format-4.0 +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 +#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 +SpaceBeforeParens: ControlStatements +#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..8f47f5e --- /dev/null +++ b/.gdbinit @@ -0,0 +1,7 @@ +set confirm off +set architecture riscv:rv64 +target remote 127.0.0.1:15234 +symbol-file build/kernel +display/12i $pc-8 +set riscv use-compressed-breakpoints yes +break *0x1000 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c6fd1c --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +.vscode +.idea +build +target +/user +link_app.S +kernel_app.ld +*.o +*.d +*.asm +*.sym diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..acaec94 --- /dev/null +++ b/Makefile @@ -0,0 +1,127 @@ +.PHONY: clean build user +all: build_kernel + +K = os + +TOOLPREFIX = riscv64-unknown-elf- +CC = $(TOOLPREFIX)gcc +AS = $(TOOLPREFIX)gcc +LD = $(TOOLPREFIX)ld +OBJCOPY = $(TOOLPREFIX)objcopy +OBJDUMP = $(TOOLPREFIX)objdump +PY = python3 +GDB = $(TOOLPREFIX)gdb +CP = cp +BUILDDIR = build +C_SRCS = $(wildcard $K/*.c) +AS_SRCS = $(wildcard $K/*.S) +C_OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(C_SRCS)))) +AS_OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(AS_SRCS)))) +OBJS = $(C_OBJS) $(AS_OBJS) + +HEADER_DEP = $(addsuffix .d, $(basename $(C_OBJS))) + +ifeq (,$(findstring link_app.o,$(OBJS))) + AS_OBJS += $(BUILDDIR)/$K/link_app.o +endif + +-include $(HEADER_DEP) + +CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb +CFLAGS += -MD +CFLAGS += -mcmodel=medany +CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax +CFLAGS += -I$K +CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) + + +LOG ?= error + +ifeq ($(LOG), error) +CFLAGS += -D LOG_LEVEL_ERROR +else ifeq ($(LOG), warn) +CFLAGS += -D LOG_LEVEL_WARN +else ifeq ($(LOG), info) +CFLAGS += -D LOG_LEVEL_INFO +else ifeq ($(LOG), debug) +CFLAGS += -D LOG_LEVEL_DEBUG +else ifeq ($(LOG), trace) +CFLAGS += -D LOG_LEVEL_TRACE +endif + +# Disable PIE when possible (for Ubuntu 16.10 toolchain) +ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) +CFLAGS += -fno-pie -no-pie +endif +ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) +CFLAGS += -fno-pie -nopie +endif + +LDFLAGS = -z max-page-size=4096 + +$(AS_OBJS): $(BUILDDIR)/$K/%.o : $K/%.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(C_OBJS): $(BUILDDIR)/$K/%.o : $K/%.c $(BUILDDIR)/$K/%.d + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(HEADER_DEP): $(BUILDDIR)/$K/%.d : $K/%.c + @mkdir -p $(@D) + @set -e; rm -f $@; $(CC) -MM $< $(INCLUDEFLAGS) > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +os/link_app.o: $K/link_app.S +os/link_app.S: scripts/pack.py + @$(PY) scripts/pack.py +os/kernel_app.ld: scripts/kernelld.py + @$(PY) scripts/kernelld.py + +build: build/kernel + +build/kernel: $(OBJS) os/kernel_app.ld + $(LD) $(LDFLAGS) -T os/kernel_app.ld -o $(BUILDDIR)/kernel $(OBJS) + $(OBJDUMP) -S $(BUILDDIR)/kernel > $(BUILDDIR)/kernel.asm + $(OBJDUMP) -t $(BUILDDIR)/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(BUILDDIR)/kernel.sym + @echo 'Build kernel done' + +clean: + rm -rf $(BUILDDIR) os/kernel_app.ld os/link_app.S + make -C user clean + +# BOARD +BOARD ?= qemu +SBI ?= rustsbi +BOOTLOADER := ./bootloader/rustsbi-qemu.bin + +QEMU = qemu-system-riscv64 +QEMUOPTS = \ + -nographic \ + -machine virt \ + -bios $(BOOTLOADER) \ + -kernel build/kernel \ + +run: build/kernel + $(QEMU) $(QEMUOPTS) + +# QEMU's gdb stub command line changed in 0.11 +QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ + then echo "-gdb tcp::15234"; \ + else echo "-s -p 15234"; fi) + +debug: build/kernel .gdbinit + $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) & + sleep 1 + $(GDB) + +CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | grep -oP 'ch\K[0-9]') + +BASE ?= 0 + +user: + make -C user CHAPTER=$(CHAPTER) BASE=$(BASE) + +test: user run + diff --git a/bootloader/rustsbi-qemu.bin b/bootloader/rustsbi-qemu.bin new file mode 100644 index 0000000..5ccab43 Binary files /dev/null and b/bootloader/rustsbi-qemu.bin differ diff --git a/os/console.c b/os/console.c new file mode 100644 index 0000000..f987a8b --- /dev/null +++ b/os/console.c @@ -0,0 +1,12 @@ +#include "console.h" +#include "sbi.h" + +void consputc(int c) +{ + console_putchar(c); +} + +void console_init() +{ + // DO NOTHING +} \ No newline at end of file diff --git a/os/console.h b/os/console.h new file mode 100644 index 0000000..7910ce5 --- /dev/null +++ b/os/console.h @@ -0,0 +1,7 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +void consputc(int); +void console_init(); + +#endif // CONSOLE_H diff --git a/os/const.h b/os/const.h new file mode 100644 index 0000000..74096d6 --- /dev/null +++ b/os/const.h @@ -0,0 +1,36 @@ +#ifndef CONST_H +#define CONST_H + +#define PAGE_SIZE (0x1000) + +enum { + STDIN = 0, + STDOUT = 1, + STDERR = 2, +}; + +// memory layout + +// the kernel expects there to be RAM +// for use by the kernel and user pages +// from physical address 0x80000000 to PHYSTOP. +#define KERNBASE 0x80200000L +#define PHYSTOP (0x80000000 + 128 * 1024 * 1024) // we have 128M memroy + +// one beyond the highest possible virtual address. +// MAXVA is actually one bit less than the max allowed by +// Sv39, to avoid having to sign-extend virtual addresses +// that have the high bit set. +#define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) + +// map the trampoline page to the highest address, +// in both user and kernel space. +#define USER_TOP (MAXVA) +#define TRAMPOLINE (USER_TOP - PGSIZE) +#define TRAPFRAME (TRAMPOLINE - PGSIZE) + +// memory layout end + +#define MAX_STR_LEN (200) + +#endif // CONST_H \ No newline at end of file diff --git a/os/defs.h b/os/defs.h new file mode 100644 index 0000000..d0dbab2 --- /dev/null +++ b/os/defs.h @@ -0,0 +1,20 @@ +#ifndef DEFS_H +#define DEFS_H + +#include "const.h" +#include "kalloc.h" +#include "log.h" +#include "printf.h" +#include "proc.h" +#include "riscv.h" +#include "sbi.h" +#include "string.h" +#include "types.h" +#include "vm.h" + +// number of elements in fixed-size array +#define NELEM(x) (sizeof(x) / sizeof((x)[0])) +#define MIN(a, b) (a < b ? a : b) +#define MAX(a, b) (a > b ? a : b) + +#endif // DEF_H diff --git a/os/entry.S b/os/entry.S new file mode 100644 index 0000000..f1b8126 --- /dev/null +++ b/os/entry.S @@ -0,0 +1,12 @@ + .section .text.entry + .globl _entry +_entry: + la sp, boot_stack_top + call main + + .section .bss.stack + .globl boot_stack +boot_stack: + .space 4096 * 16 + .globl boot_stack_top +boot_stack_top: diff --git a/os/kalloc.c b/os/kalloc.c new file mode 100644 index 0000000..6f7012d --- /dev/null +++ b/os/kalloc.c @@ -0,0 +1,57 @@ +#include "kalloc.h" +#include "defs.h" +#include "riscv.h" + +extern char ekernel[]; + +struct linklist { + struct linklist *next; +}; + +struct { + struct linklist *freelist; +} kmem; + +void freerange(void *pa_start, void *pa_end) +{ + char *p; + p = (char *)PGROUNDUP((uint64)pa_start); + for (; p + PGSIZE <= (char *)pa_end; p += PGSIZE) + kfree(p); +} + +void kinit() +{ + freerange(ekernel, (void *)PHYSTOP); +} + +// Free the page of physical memory pointed at by v, +// which normally should have been returned by a +// call to kalloc(). (The exception is when +// initializing the allocator; see kinit above.) +void kfree(void *pa) +{ + struct linklist *l; + if (((uint64)pa % PGSIZE) != 0 || (char *)pa < ekernel || + (uint64)pa >= PHYSTOP) + panic("kfree"); + // Fill with junk to catch dangling refs. + memset(pa, 1, PGSIZE); + l = (struct linklist *)pa; + l->next = kmem.freelist; + kmem.freelist = l; +} + +// Allocate one 4096-byte page of physical memory. +// Returns a pointer that the kernel can use. +// Returns 0 if the memory cannot be allocated. +void *kalloc(void) +{ + struct linklist *l; + l = kmem.freelist; + if (l) { + kmem.freelist = l->next; + memset((char *)l, 5, PGSIZE); // fill with junk + } + return (void *)l; +} \ No newline at end of file diff --git a/os/kalloc.h b/os/kalloc.h new file mode 100644 index 0000000..c6936b6 --- /dev/null +++ b/os/kalloc.h @@ -0,0 +1,8 @@ +#ifndef KALLOC_H +#define KALLOC_H + +void *kalloc(void); +void kfree(void *); +void kinit(void); + +#endif // KALLOC_H \ No newline at end of file diff --git a/os/kernel.ld b/os/kernel.ld new file mode 100644 index 0000000..24069a8 --- /dev/null +++ b/os/kernel.ld @@ -0,0 +1,51 @@ +OUTPUT_ARCH(riscv) +ENTRY(_entry) +BASE_ADDRESS = 0x80200000; + +SECTIONS +{ + . = BASE_ADDRESS; + skernel = .; + + s_text = .; + .text : { + *(.text.entry) + *(.text .text.*) + . = ALIGN(0x1000); + *(trampsec) + . = ALIGN(0x1000); + } + + . = ALIGN(4K); + e_text = .; + s_rodata = .; + .rodata : { + *(.rodata .rodata.*) + } + + . = ALIGN(4K); + e_rodata = .; + s_data = .; + .data : { + *(.data.apps) + *(.data .data.*) + *(.sdata .sdata.*) + } + + . = ALIGN(4K); + e_data = .; + .bss : { + *(.bss.stack) + s_bss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + } + + . = ALIGN(4K); + e_bss = .; + ekernel = .; + + /DISCARD/ : { + *(.eh_frame) + } +} \ No newline at end of file diff --git a/os/kernelld.py b/os/kernelld.py new file mode 100644 index 0000000..33beb91 --- /dev/null +++ b/os/kernelld.py @@ -0,0 +1,68 @@ +import os + +TARGET_DIR = "../user/target/" + +if __name__ == '__main__': + f = open("kernel_app.ld", mode="w") + apps = os.listdir(TARGET_DIR) + f.write( +'''OUTPUT_ARCH(riscv) +ENTRY(_entry) +BASE_ADDRESS = 0x80200000; + +SECTIONS +{ + . = BASE_ADDRESS; + skernel = .; + + s_text = .; + .text : { + *(.text.entry) + *(.text .text.*) + . = ALIGN(0x1000); + *(trampsec) + . = ALIGN(0x1000); + } + + . = ALIGN(4K); + e_text = .; + s_rodata = .; + .rodata : { + *(.rodata .rodata.*) + } + + . = ALIGN(4K); + e_rodata = .; + s_data = .; + .data : { + *(.data) +''') + for (idx, _) in enumerate(apps): + f.write(' . = ALIGN(0x1000);\n') + f.write(' *(.data.app{})\n'.format(idx)) + f.write( +''' + *(.data.*) + *(.sdata .sdata.*) + } + + . = ALIGN(4K); + e_data = .; + .bss : { + *(.bss.stack) + s_bss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + } + + . = ALIGN(4K); + e_bss = .; + ekernel = .; + + /DISCARD/ : { + *(.eh_frame) + } +} +''') + f.close() + diff --git a/os/loader.c b/os/loader.c new file mode 100644 index 0000000..8dd830e --- /dev/null +++ b/os/loader.c @@ -0,0 +1,75 @@ +#include "loader.h" +#include "defs.h" +#include "trap.h" + +static int app_num; +static uint64 *app_info_ptr; +extern char _app_num[]; + +// Count finished programs. If all apps exited, shutdown. +int finished() +{ + static int fin = 0; + if (++fin >= app_num) + panic("all apps over"); + return 0; +} + +// Get user progs' infomation through pre-defined symbol in `link_app.S` +void loader_init() +{ + app_info_ptr = (uint64 *)_app_num; + app_num = *app_info_ptr; + app_info_ptr++; +} + +pagetable_t bin_loader(uint64 start, uint64 end, struct proc *p) +{ + pagetable_t pg = uvmcreate(); + if (mappages(pg, TRAPFRAME, PGSIZE, (uint64)p->trapframe, + PTE_R | PTE_W) < 0) { + panic("mappages fail"); + } + if (!PGALIGNED(start)) { + panic("user program not aligned, start = %p", start); + } + if (!PGALIGNED(end)) { + // Fix in ch5 + warnf("Some kernel data maybe mapped to user, start = %p, end = %p", + start, end); + } + end = PGROUNDUP(end); + uint64 length = end - start; + if (mappages(pg, BASE_ADDRESS, length, start, + PTE_U | PTE_R | PTE_W | PTE_X) != 0) { + panic("mappages fail"); + } + p->pagetable = pg; + uint64 ustack_bottom_vaddr = BASE_ADDRESS + length + PAGE_SIZE; + if (USTACK_SIZE != PAGE_SIZE) { + // Fix in ch5 + panic("Unsupported"); + } + mappages(pg, ustack_bottom_vaddr, USTACK_SIZE, (uint64)kalloc(), + PTE_U | PTE_R | PTE_W | PTE_X); + p->ustack = ustack_bottom_vaddr; + p->trapframe->epc = BASE_ADDRESS; + p->trapframe->sp = p->ustack + USTACK_SIZE; + p->max_page = PGROUNDUP(p->ustack + USTACK_SIZE - 1) / PAGE_SIZE; + return pg; +} + +// load all apps and init the corresponding `proc` structure. +int run_all_app() +{ + for (int i = 0; i < app_num; ++i) { + struct proc *p = allocproc(); + tracef("load app %d", i); + bin_loader(app_info_ptr[i], app_info_ptr[i + 1], p); + p->state = RUNNABLE; + /* + * LAB1: you may need to initialize your new fields of proc here + */ + } + return 0; +} \ No newline at end of file diff --git a/os/loader.h b/os/loader.h new file mode 100644 index 0000000..bdd8e29 --- /dev/null +++ b/os/loader.h @@ -0,0 +1,16 @@ +#ifndef LOADER_H +#define LOADER_H + +#include "const.h" +#include "types.h" + +int finished(); +void loader_init(); +int run_all_app(); + +#define BASE_ADDRESS (0x1000) +#define USTACK_SIZE (PAGE_SIZE) +#define KSTACK_SIZE (PAGE_SIZE) +#define TRAP_PAGE_SIZE (PAGE_SIZE) + +#endif // LOADER_H \ No newline at end of file diff --git a/os/log.h b/os/log.h new file mode 100644 index 0000000..06efe90 --- /dev/null +++ b/os/log.h @@ -0,0 +1,120 @@ +#ifndef LOG_H +#define LOG_H + +extern void printf(char *, ...); +extern int threadid(); +extern void dummy(int, ...); +extern void shutdown(); + +#if defined(LOG_LEVEL_ERROR) + +#define USE_LOG_ERROR + +#endif // LOG_LEVEL_ERROR + +#if defined(LOG_LEVEL_WARN) + +#define USE_LOG_ERROR +#define USE_LOG_WARN + +#endif // LOG_LEVEL_ERROR + +#if defined(LOG_LEVEL_INFO) + +#define USE_LOG_ERROR +#define USE_LOG_WARN +#define USE_LOG_INFO + +#endif // LOG_LEVEL_INFO + +#if defined(LOG_LEVEL_DEBUG) + +#define USE_LOG_ERROR +#define USE_LOG_WARN +#define USE_LOG_INFO +#define USE_LOG_DEBUG + +#endif // LOG_LEVEL_DEBUG + +#if defined(LOG_LEVEL_TRACE) + +#define USE_LOG_ERROR +#define USE_LOG_WARN +#define USE_LOG_INFO +#define USE_LOG_DEBUG +#define USE_LOG_TRACE + +#endif // LOG_LEVEL_TRACE + +enum LOG_COLOR { + RED = 31, + GREEN = 32, + BLUE = 34, + GRAY = 90, + YELLOW = 93, +}; + +#if defined(USE_LOG_ERROR) +#define errorf(fmt, ...) \ + do { \ + int tid = threadid(); \ + printf("\x1b[%dm[%s %d]" fmt "\x1b[0m\n", RED, "ERROR", tid, \ + ##__VA_ARGS__); \ + } while (0) +#else +#define errorf(fmt, ...) dummy(0, ##__VA_ARGS__) +#endif // USE_LOG_ERROR + +#if defined(USE_LOG_WARN) +#define warnf(fmt, ...) \ + do { \ + int tid = threadid(); \ + printf("\x1b[%dm[%s %d]" fmt "\x1b[0m\n", YELLOW, "WARN", tid, \ + ##__VA_ARGS__); \ + } while (0) +#else +#define warnf(fmt, ...) dummy(0, ##__VA_ARGS__) +#endif // USE_LOG_WARN + +#if defined(USE_LOG_INFO) +#define infof(fmt, ...) \ + do { \ + int tid = threadid(); \ + printf("\x1b[%dm[%s %d]" fmt "\x1b[0m\n", BLUE, "INFO", tid, \ + ##__VA_ARGS__); \ + } while (0) +#else +#define infof(fmt, ...) dummy(0, ##__VA_ARGS__) +#endif // USE_LOG_INFO + +#if defined(USE_LOG_DEBUG) +#define debugf(fmt, ...) \ + do { \ + int tid = threadid(); \ + printf("\x1b[%dm[%s %d]" fmt "\x1b[0m\n", GREEN, "DEBUG", tid, \ + ##__VA_ARGS__); \ + } while (0) +#else +#define debugf(fmt, ...) dummy(0, ##__VA_ARGS__) +#endif // USE_LOG_DEBUG + +#if defined(USE_LOG_TRACE) +#define tracef(fmt, ...) \ + do { \ + int tid = threadid(); \ + printf("\x1b[%dm[%s %d]" fmt "\x1b[0m\n", GRAY, "TRACE", tid, \ + ##__VA_ARGS__); \ + } while (0) +#else +#define tracef(fmt, ...) dummy(0, ##__VA_ARGS__) +#endif // USE_LOG_TRACE + +#define panic(fmt, ...) \ + do { \ + int tid = threadid(); \ + printf("\x1b[%dm[%s %d] %s:%d: " fmt "\x1b[0m\n", RED, \ + "PANIC", tid, __FILE__, __LINE__, ##__VA_ARGS__); \ + shutdown(); \ + } while (0) + +#endif //! LOG_H diff --git a/os/main.c b/os/main.c new file mode 100644 index 0000000..714ab28 --- /dev/null +++ b/os/main.c @@ -0,0 +1,27 @@ +#include "console.h" +#include "defs.h" +#include "loader.h" +#include "timer.h" +#include "trap.h" + +void clean_bss() +{ + extern char s_bss[]; + extern char e_bss[]; + memset(s_bss, 0, e_bss - s_bss); +} + +void main() +{ + clean_bss(); + printf("hello world!\n"); + proc_init(); + kinit(); + kvm_init(); + loader_init(); + trap_init(); + timer_init(); + run_all_app(); + infof("start scheduler!"); + scheduler(); +} \ No newline at end of file diff --git a/os/pack.py b/os/pack.py new file mode 100644 index 0000000..a65985e --- /dev/null +++ b/os/pack.py @@ -0,0 +1,42 @@ +import os + +TARGET_DIR = "../user/target" + +if __name__ == '__main__': + f = open("link_app.S", mode="w") + apps = os.listdir(TARGET_DIR) + apps.sort() + f.write( +''' .align 4 + .section .data + .global _app_num +_app_num: + .quad {} +'''.format(len(apps)) + ) + + for (idx, _) in enumerate(apps): + f.write(' .quad app_{}_start\n'.format(idx)) + f.write(' .quad app_{}_end\n'.format(len(apps) - 1)) + + f.write( +''' + .global _app_names +_app_names: +'''); + + for app in apps: + app = app[:app.find('.')] + f.write(" .string \"" + app + "\"\n") + + for (idx, app) in enumerate(apps): + f.write( +''' + .section .data.app{0} + .global app_{0}_start +app_{0}_start: + .incbin "{1}" +'''.format(idx, TARGET_DIR + app) + ) + f.write('app_{}_end:\n\n'.format(len(apps) - 1)) + f.close() \ No newline at end of file diff --git a/os/printf.c b/os/printf.c new file mode 100644 index 0000000..63c6705 --- /dev/null +++ b/os/printf.c @@ -0,0 +1,83 @@ +#include +#include "console.h" +#include "defs.h" +static char digits[] = "0123456789abcdef"; + +static void printint(int xx, int base, int sign) +{ + char buf[16]; + int i; + uint x; + + if (sign && (sign = xx < 0)) + x = -xx; + else + x = xx; + + i = 0; + do { + buf[i++] = digits[x % base]; + } while ((x /= base) != 0); + + if (sign) + buf[i++] = '-'; + + while (--i >= 0) + consputc(buf[i]); +} + +static void printptr(uint64 x) +{ + int i; + consputc('0'); + consputc('x'); + for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) + consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); +} + +// Print to the console. only understands %d, %x, %p, %s. +void printf(char *fmt, ...) +{ + va_list ap; + int i, c; + char *s; + + if (fmt == 0) + panic("null fmt"); + + va_start(ap, fmt); + for (i = 0; (c = fmt[i] & 0xff) != 0; i++) { + if (c != '%') { + consputc(c); + continue; + } + c = fmt[++i] & 0xff; + if (c == 0) + break; + switch (c) { + case 'd': + printint(va_arg(ap, int), 10, 1); + break; + case 'x': + printint(va_arg(ap, int), 16, 1); + break; + case 'p': + printptr(va_arg(ap, uint64)); + break; + case 's': + if ((s = va_arg(ap, char *)) == 0) + s = "(null)"; + for (; *s; s++) + consputc(*s); + break; + case '%': + consputc('%'); + break; + default: + // Print unknown % sequence to draw attention. + consputc('%'); + consputc(c); + break; + } + } +} \ No newline at end of file diff --git a/os/printf.h b/os/printf.h new file mode 100644 index 0000000..d9c9051 --- /dev/null +++ b/os/printf.h @@ -0,0 +1,6 @@ +#ifndef PRINTF_H +#define PRINTF_H + +void printf(char *, ...); + +#endif // PRINTF_H diff --git a/os/proc.c b/os/proc.c new file mode 100644 index 0000000..31875e8 --- /dev/null +++ b/os/proc.c @@ -0,0 +1,133 @@ +#include "proc.h" +#include "defs.h" +#include "loader.h" +#include "trap.h" +#include "vm.h" + +struct proc pool[NPROC]; +__attribute__((aligned(16))) char kstack[NPROC][PAGE_SIZE]; +__attribute__((aligned(4096))) char trapframe[NPROC][TRAP_PAGE_SIZE]; + +extern char boot_stack_top[]; +struct proc *current_proc; +struct proc idle; + +int threadid() +{ + return curr_proc()->pid; +} + +struct proc *curr_proc() +{ + return current_proc; +} + +// initialize the proc table at boot time. +void proc_init(void) +{ + struct proc *p; + for (p = pool; p < &pool[NPROC]; p++) { + p->state = UNUSED; + p->kstack = (uint64)kstack[p - pool]; + p->trapframe = (struct trapframe *)trapframe[p - pool]; + /* + * LAB1: you may need to initialize your new fields of proc here + */ + } + idle.kstack = (uint64)boot_stack_top; + idle.pid = 0; + current_proc = &idle; +} + +int allocpid() +{ + static int PID = 1; + return PID++; +} + +// Look in the process table for an UNUSED proc. +// If found, initialize state required to run in the kernel. +// If there are no free procs, or a memory allocation fails, return 0. +struct proc *allocproc(void) +{ + struct proc *p; + for (p = pool; p < &pool[NPROC]; p++) { + if (p->state == UNUSED) { + goto found; + } + } + return 0; + +found: + p->pid = allocpid(); + p->state = USED; + p->pagetable = 0; + p->ustack = 0; + p->max_page = 0; + memset(&p->context, 0, sizeof(p->context)); + memset((void *)p->kstack, 0, KSTACK_SIZE); + memset((void *)p->trapframe, 0, TRAP_PAGE_SIZE); + p->context.ra = (uint64)usertrapret; + p->context.sp = p->kstack + KSTACK_SIZE; + return p; +} + +// Scheduler never returns. It loops, doing: +// - choose a process to run. +// - swtch to start running that process. +// - eventually that process transfers control +// via swtch back to the scheduler. +void scheduler(void) +{ + struct proc *p; + for (;;) { + for (p = pool; p < &pool[NPROC]; p++) { + if (p->state == RUNNABLE) { + /* + * LAB1: you may need to init proc start time here + */ + p->state = RUNNING; + current_proc = p; + swtch(&idle.context, &p->context); + } + } + } +} + +// Switch to scheduler. Must hold only p->lock +// and have changed proc->state. Saves and restores +// intena because intena is a property of this +// kernel thread, not this CPU. It should +// be proc->intena and proc->noff, but that would +// break in the few places where a lock is held but +// there's no process. +void sched(void) +{ + struct proc *p = curr_proc(); + if (p->state == RUNNING) + panic("sched running"); + swtch(&p->context, &idle.context); +} + +// Give up the CPU for one scheduling round. +void yield(void) +{ + current_proc->state = RUNNABLE; + sched(); +} + +void freeproc(struct proc *p) +{ + p->state = UNUSED; + // uvmfree(p->pagetable, p->max_page); +} + +// Exit the current process. +void exit(int code) +{ + struct proc *p = curr_proc(); + infof("proc %d exit with %d", p->pid, code); + freeproc(p); + finished(); + sched(); +} diff --git a/os/proc.h b/os/proc.h new file mode 100644 index 0000000..01d4834 --- /dev/null +++ b/os/proc.h @@ -0,0 +1,60 @@ +#ifndef PROC_H +#define PROC_H + +#include "riscv.h" +#include "types.h" + +#define NPROC (16) + +// Saved registers for kernel context switches. +struct context { + uint64 ra; + uint64 sp; + + // callee-saved + uint64 s0; + uint64 s1; + uint64 s2; + uint64 s3; + uint64 s4; + uint64 s5; + uint64 s6; + uint64 s7; + uint64 s8; + uint64 s9; + uint64 s10; + uint64 s11; +}; + +enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; + +// Per-process state +struct proc { + enum procstate state; // Process state + int pid; // Process ID + pagetable_t pagetable; // User page table + uint64 ustack; + uint64 kstack; // Virtual address of kernel stack + struct trapframe *trapframe; // data page for trampoline.S + struct context context; // swtch() here to run process + uint64 max_page; + /* + * LAB1: you may need to add some new fields here + */ +}; + +/* +* LAB1: you may need to define struct for TaskInfo here +*/ + +struct proc *curr_proc(); +void exit(int); +void proc_init(); +void scheduler() __attribute__((noreturn)); +void sched(); +void yield(); +struct proc *allocproc(); +// swtch.S +void swtch(struct context *, struct context *); + +#endif // PROC_H \ No newline at end of file diff --git a/os/riscv.h b/os/riscv.h new file mode 100644 index 0000000..affaeef --- /dev/null +++ b/os/riscv.h @@ -0,0 +1,324 @@ +#ifndef RISCV_H +#define RISCV_H + +#include "types.h" + +// which hart (core) is this? +static inline uint64 r_mhartid() +{ + uint64 x; + asm volatile("csrr %0, mhartid" : "=r"(x)); + return x; +} + +// Machine Status Register, mstatus + +#define MSTATUS_MPP_MASK (3L << 11) // previous mode. +#define MSTATUS_MPP_M (3L << 11) +#define MSTATUS_MPP_S (1L << 11) +#define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. + +static inline uint64 r_mstatus() +{ + uint64 x; + asm volatile("csrr %0, mstatus" : "=r"(x)); + return x; +} + +static inline void w_mstatus(uint64 x) +{ + asm volatile("csrw mstatus, %0" : : "r"(x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void w_mepc(uint64 x) +{ + asm volatile("csrw mepc, %0" : : "r"(x)); +} + +// Supervisor Status Register, sstatus + +#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User +#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable +#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable +#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable +#define SSTATUS_UIE (1L << 0) // User Interrupt Enable + +static inline uint64 r_sstatus() +{ + uint64 x; + asm volatile("csrr %0, sstatus" : "=r"(x)); + return x; +} + +static inline void w_sstatus(uint64 x) +{ + asm volatile("csrw sstatus, %0" : : "r"(x)); +} + +// Supervisor Interrupt Pending +static inline uint64 r_sip() +{ + uint64 x; + asm volatile("csrr %0, sip" : "=r"(x)); + return x; +} + +static inline void w_sip(uint64 x) +{ + asm volatile("csrw sip, %0" : : "r"(x)); +} + +// Supervisor Interrupt Enable +#define SIE_SEIE (1L << 9) // external +#define SIE_STIE (1L << 5) // timer +#define SIE_SSIE (1L << 1) // software +static inline uint64 r_sie() +{ + uint64 x; + asm volatile("csrr %0, sie" : "=r"(x)); + return x; +} + +static inline void w_sie(uint64 x) +{ + asm volatile("csrw sie, %0" : : "r"(x)); +} + +// Machine-mode Interrupt Enable +#define MIE_MEIE (1L << 11) // external +#define MIE_MTIE (1L << 7) // timer +#define MIE_MSIE (1L << 3) // software +static inline uint64 r_mie() +{ + uint64 x; + asm volatile("csrr %0, mie" : "=r"(x)); + return x; +} + +static inline void w_mie(uint64 x) +{ + asm volatile("csrw mie, %0" : : "r"(x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void w_sepc(uint64 x) +{ + asm volatile("csrw sepc, %0" : : "r"(x)); +} + +static inline uint64 r_sepc() +{ + uint64 x; + asm volatile("csrr %0, sepc" : "=r"(x)); + return x; +} + +// Machine Exception Delegation +static inline uint64 r_medeleg() +{ + uint64 x; + asm volatile("csrr %0, medeleg" : "=r"(x)); + return x; +} + +static inline void w_medeleg(uint64 x) +{ + asm volatile("csrw medeleg, %0" : : "r"(x)); +} + +// Machine Interrupt Delegation +static inline uint64 r_mideleg() +{ + uint64 x; + asm volatile("csrr %0, mideleg" : "=r"(x)); + return x; +} + +static inline void w_mideleg(uint64 x) +{ + asm volatile("csrw mideleg, %0" : : "r"(x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void w_stvec(uint64 x) +{ + asm volatile("csrw stvec, %0" : : "r"(x)); +} + +static inline uint64 r_stvec() +{ + uint64 x; + asm volatile("csrr %0, stvec" : "=r"(x)); + return x; +} + +// Machine-mode interrupt vector +static inline void w_mtvec(uint64 x) +{ + asm volatile("csrw mtvec, %0" : : "r"(x)); +} + +// use riscv's sv39 page table scheme. +#define SATP_SV39 (8L << 60) + +#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) + +// supervisor address translation and protection; +// holds the address of the page table. +static inline void w_satp(uint64 x) +{ + asm volatile("csrw satp, %0" : : "r"(x)); +} + +static inline uint64 r_satp() +{ + uint64 x; + asm volatile("csrr %0, satp" : "=r"(x)); + return x; +} + +// Supervisor Scratch register, for early trap handler in trampoline.S. +static inline void w_sscratch(uint64 x) +{ + asm volatile("csrw sscratch, %0" : : "r"(x)); +} + +static inline void w_mscratch(uint64 x) +{ + asm volatile("csrw mscratch, %0" : : "r"(x)); +} + +// Supervisor Trap Cause +static inline uint64 r_scause() +{ + uint64 x; + asm volatile("csrr %0, scause" : "=r"(x)); + return x; +} + +// Supervisor Trap Value +static inline uint64 r_stval() +{ + uint64 x; + asm volatile("csrr %0, stval" : "=r"(x)); + return x; +} + +// Machine-mode Counter-Enable +static inline void w_mcounteren(uint64 x) +{ + asm volatile("csrw mcounteren, %0" : : "r"(x)); +} + +static inline uint64 r_mcounteren() +{ + uint64 x; + asm volatile("csrr %0, mcounteren" : "=r"(x)); + return x; +} + +// machine-mode cycle counter +static inline uint64 r_time() +{ + uint64 x; + asm volatile("csrr %0, time" : "=r"(x)); + return x; +} + +// enable device interrupts +static inline void intr_on() +{ + w_sstatus(r_sstatus() | SSTATUS_SIE); +} + +// disable device interrupts +static inline void intr_off() +{ + w_sstatus(r_sstatus() & ~SSTATUS_SIE); +} + +// are device interrupts enabled? +static inline int intr_get() +{ + uint64 x = r_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline uint64 r_sp() +{ + uint64 x; + asm volatile("mv %0, sp" : "=r"(x)); + return x; +} + +// read and write tp, the thread pointer, which holds +// this core's hartid (core number), the index into cpus[]. +static inline uint64 r_tp() +{ + uint64 x; + asm volatile("mv %0, tp" : "=r"(x)); + return x; +} + +static inline void w_tp(uint64 x) +{ + asm volatile("mv tp, %0" : : "r"(x)); +} + +static inline uint64 r_ra() +{ + uint64 x; + asm volatile("mv %0, ra" : "=r"(x)); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#define PGSIZE 4096 // bytes per page +#define PGSHIFT 12 // bits of offset within a page + +#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~(PGSIZE - 1)) +#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE - 1)) +#define PGALIGNED(a) (((a) & (PGSIZE - 1)) == 0) + +#define PTE_V (1L << 0) // valid +#define PTE_R (1L << 1) +#define PTE_W (1L << 2) +#define PTE_X (1L << 3) +#define PTE_U (1L << 4) // 1 -> user can access + +// shift a physical address to the right place for a PTE. +#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) + +#define PTE2PA(pte) (((pte) >> 10) << 12) + +#define PTE_FLAGS(pte) ((pte)&0x3FF) + +// extract the three 9-bit page table indices from a virtual address. +#define PXMASK 0x1FF // 9 bits +#define PXSHIFT(level) (PGSHIFT + (9 * (level))) +#define PX(level, va) ((((uint64)(va)) >> PXSHIFT(level)) & PXMASK) + +// one beyond the highest possible virtual address. +// MAXVA is actually one bit less than the max allowed by +// Sv39, to avoid having to sign-extend virtual addresses +// that have the high bit set. +#define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) + +typedef uint64 pte_t; +typedef uint64 pde_t; +typedef uint64 *pagetable_t; // 512 PTEs + +#endif // RISCV_H \ No newline at end of file diff --git a/os/sbi.c b/os/sbi.c new file mode 100644 index 0000000..5ded34d --- /dev/null +++ b/os/sbi.c @@ -0,0 +1,44 @@ +#include "sbi.h" +#include "types.h" +const uint64 SBI_SET_TIMER = 0; +const uint64 SBI_CONSOLE_PUTCHAR = 1; +const uint64 SBI_CONSOLE_GETCHAR = 2; +const uint64 SBI_CLEAR_IPI = 3; +const uint64 SBI_SEND_IPI = 4; +const uint64 SBI_REMOTE_FENCE_I = 5; +const uint64 SBI_REMOTE_SFENCE_VMA = 6; +const uint64 SBI_REMOTE_SFENCE_VMA_ASID = 7; +const uint64 SBI_SHUTDOWN = 8; + +int inline sbi_call(uint64 which, uint64 arg0, uint64 arg1, uint64 arg2) +{ + register uint64 a0 asm("a0") = arg0; + register uint64 a1 asm("a1") = arg1; + register uint64 a2 asm("a2") = arg2; + register uint64 a7 asm("a7") = which; + asm volatile("ecall" + : "=r"(a0) + : "r"(a0), "r"(a1), "r"(a2), "r"(a7) + : "memory"); + return a0; +} + +void console_putchar(int c) +{ + sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); +} + +int console_getchar() +{ + return sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0); +} + +void shutdown() +{ + sbi_call(SBI_SHUTDOWN, 0, 0, 0); +} + +void set_timer(uint64 stime) +{ + sbi_call(SBI_SET_TIMER, stime, 0, 0); +} \ No newline at end of file diff --git a/os/sbi.h b/os/sbi.h new file mode 100644 index 0000000..43afd01 --- /dev/null +++ b/os/sbi.h @@ -0,0 +1,11 @@ +#ifndef SBI_H +#define SBI_H + +#include "types.h" + +void console_putchar(int); +int console_getchar(); +void shutdown(); +void set_timer(uint64 stime); + +#endif // SBI_H diff --git a/os/string.c b/os/string.c new file mode 100644 index 0000000..fdb38af --- /dev/null +++ b/os/string.c @@ -0,0 +1,100 @@ +#include "string.h" +#include "types.h" + +void *memset(void *dst, int c, uint n) +{ + char *cdst = (char *)dst; + int i; + for (i = 0; i < n; i++) { + cdst[i] = c; + } + return dst; +} + +int memcmp(const void *v1, const void *v2, uint n) +{ + const uchar *s1, *s2; + + s1 = v1; + s2 = v2; + while (n-- > 0) { + if (*s1 != *s2) + return *s1 - *s2; + s1++, s2++; + } + + return 0; +} + +void *memmove(void *dst, const void *src, uint n) +{ + const char *s; + char *d; + + s = src; + d = dst; + if (s < d && s + n > d) { + s += n; + d += n; + while (n-- > 0) + *--d = *--s; + } else + while (n-- > 0) + *d++ = *s++; + + return dst; +} + +// memcpy exists to placate GCC. Use memmove. +void *memcpy(void *dst, const void *src, uint n) +{ + return memmove(dst, src, n); +} + +int strncmp(const char *p, const char *q, uint n) +{ + while (n > 0 && *p && *p == *q) + n--, p++, q++; + if (n == 0) + return 0; + return (uchar)*p - (uchar)*q; +} + +char *strncpy(char *s, const char *t, int n) +{ + char *os; + + os = s; + while (n-- > 0 && (*s++ = *t++) != 0) + ; + while (n-- > 0) + *s++ = 0; + return os; +} + +// Like strncpy but guaranteed to NUL-terminate. +char *safestrcpy(char *s, const char *t, int n) +{ + char *os; + + os = s; + if (n <= 0) + return os; + while (--n > 0 && (*s++ = *t++) != 0) + ; + *s = 0; + return os; +} + +int strlen(const char *s) +{ + int n; + + for (n = 0; s[n]; n++) + ; + return n; +} + +void dummy(int _, ...) +{ +} \ No newline at end of file diff --git a/os/string.h b/os/string.h new file mode 100644 index 0000000..274f6f3 --- /dev/null +++ b/os/string.h @@ -0,0 +1,14 @@ +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +int memcmp(const void *, const void *, uint); +void *memmove(void *, const void *, uint); +void *memset(void *, int, uint); +char *safestrcpy(char *, const char *, int); +int strlen(const char *); +int strncmp(const char *, const char *, uint); +char *strncpy(char *, const char *, int); + +#endif // STRING_H \ No newline at end of file diff --git a/os/switch.S b/os/switch.S new file mode 100644 index 0000000..d717a12 --- /dev/null +++ b/os/switch.S @@ -0,0 +1,40 @@ +# Context switch +# +# void swtch(struct context *old, struct context *new); +# +# Save current registers in old. Load from new. + + +.globl swtch +swtch: + sd ra, 0(a0) + sd sp, 8(a0) + sd s0, 16(a0) + sd s1, 24(a0) + sd s2, 32(a0) + sd s3, 40(a0) + sd s4, 48(a0) + sd s5, 56(a0) + sd s6, 64(a0) + sd s7, 72(a0) + sd s8, 80(a0) + sd s9, 88(a0) + sd s10, 96(a0) + sd s11, 104(a0) + + ld ra, 0(a1) + ld sp, 8(a1) + ld s0, 16(a1) + ld s1, 24(a1) + ld s2, 32(a1) + ld s3, 40(a1) + ld s4, 48(a1) + ld s5, 56(a1) + ld s6, 64(a1) + ld s7, 72(a1) + ld s8, 80(a1) + ld s9, 88(a1) + ld s10, 96(a1) + ld s11, 104(a1) + + ret \ No newline at end of file diff --git a/os/syscall.c b/os/syscall.c new file mode 100644 index 0000000..7d7afe6 --- /dev/null +++ b/os/syscall.c @@ -0,0 +1,91 @@ +#include "syscall.h" +#include "defs.h" +#include "loader.h" +#include "syscall_ids.h" +#include "timer.h" +#include "trap.h" + +uint64 sys_write(int fd, uint64 va, uint len) +{ + debugf("sys_write fd = %d va = %x, len = %d", fd, va, len); + if (fd != STDOUT) + return -1; + struct proc *p = curr_proc(); + char str[MAX_STR_LEN]; + int size = copyinstr(p->pagetable, str, va, MIN(len, MAX_STR_LEN)); + debugf("size = %d", size); + for (int i = 0; i < size; ++i) { + console_putchar(str[i]); + } + return size; +} + +__attribute__((noreturn)) void sys_exit(int code) +{ + exit(code); + __builtin_unreachable(); +} + +uint64 sys_sched_yield() +{ + yield(); + return 0; +} + +uint64 sys_gettimeofday(TimeVal *val, int _tz) // TODO: implement sys_gettimeofday in pagetable. (VA to PA) +{ + // YOUR CODE + val->sec = 0; + val->usec = 0; + + /* The code in `ch3` will leads to memory bugs*/ + + // uint64 cycle = get_cycle(); + // val->sec = cycle / CPU_FREQ; + // val->usec = (cycle % CPU_FREQ) * 1000000 / CPU_FREQ; + return 0; +} + +// TODO: add support for mmap and munmap syscall. +// hint: read through docstrings in vm.c. Watching CH4 video may also help. +// Note the return value and PTE flags (especially U,X,W,R) +/* +* LAB1: you may need to define sys_task_info here +*/ + +extern char trap_page[]; + +void syscall() +{ + struct trapframe *trapframe = curr_proc()->trapframe; + int id = trapframe->a7, ret; + uint64 args[6] = { trapframe->a0, trapframe->a1, trapframe->a2, + trapframe->a3, trapframe->a4, trapframe->a5 }; + tracef("syscall %d args = [%x, %x, %x, %x, %x, %x]", id, args[0], + args[1], args[2], args[3], args[4], args[5]); + /* + * LAB1: you may need to update syscall counter for task info here + */ + switch (id) { + case SYS_write: + ret = sys_write(args[0], args[1], args[2]); + break; + case SYS_exit: + sys_exit(args[0]); + // __builtin_unreachable(); + case SYS_sched_yield: + ret = sys_sched_yield(); + break; + case SYS_gettimeofday: + ret = sys_gettimeofday((TimeVal *)args[0], args[1]); + break; + /* + * LAB1: you may need to add SYS_taskinfo case here + */ + default: + ret = -1; + errorf("unknown syscall %d", id); + } + trapframe->a0 = ret; + tracef("syscall ret %d", ret); +} diff --git a/os/syscall.h b/os/syscall.h new file mode 100644 index 0000000..2a46a16 --- /dev/null +++ b/os/syscall.h @@ -0,0 +1,6 @@ +#ifndef SYSCALL_H +#define SYSCALL_H + +void syscall(); + +#endif // SYSCALL_H diff --git a/os/syscall_ids.h b/os/syscall_ids.h new file mode 100644 index 0000000..0a1b397 --- /dev/null +++ b/os/syscall_ids.h @@ -0,0 +1,298 @@ +#define SYS_io_setup 0 +#define SYS_io_destroy 1 +#define SYS_io_submit 2 +#define SYS_io_cancel 3 +#define SYS_io_getevents 4 +#define SYS_setxattr 5 +#define SYS_lsetxattr 6 +#define SYS_fsetxattr 7 +#define SYS_getxattr 8 +#define SYS_lgetxattr 9 +#define SYS_fgetxattr 10 +#define SYS_listxattr 11 +#define SYS_llistxattr 12 +#define SYS_flistxattr 13 +#define SYS_removexattr 14 +#define SYS_lremovexattr 15 +#define SYS_fremovexattr 16 +#define SYS_getcwd 17 +#define SYS_lookup_dcookie 18 +#define SYS_eventfd2 19 +#define SYS_epoll_create1 20 +#define SYS_epoll_ctl 21 +#define SYS_epoll_pwait 22 +#define SYS_dup 23 +#define SYS_dup3 24 +#define SYS_fcntl 25 +#define SYS_inotify_init1 26 +#define SYS_inotify_add_watch 27 +#define SYS_inotify_rm_watch 28 +#define SYS_ioctl 29 +#define SYS_ioprio_set 30 +#define SYS_ioprio_get 31 +#define SYS_flock 32 +#define SYS_mknodat 33 +#define SYS_mkdirat 34 +#define SYS_unlinkat 35 +#define SYS_symlinkat 36 +#define SYS_linkat 37 +#define SYS_umount2 39 +#define SYS_mount 40 +#define SYS_pivot_root 41 +#define SYS_nfsservctl 42 +#define SYS_statfs 43 +#define SYS_fstatfs 44 +#define SYS_truncate 45 +#define SYS_ftruncate 46 +#define SYS_fallocate 47 +#define SYS_faccessat 48 +#define SYS_chdir 49 +#define SYS_fchdir 50 +#define SYS_chroot 51 +#define SYS_fchmod 52 +#define SYS_fchmodat 53 +#define SYS_fchownat 54 +#define SYS_fchown 55 +#define SYS_openat 56 +#define SYS_close 57 +#define SYS_vhangup 58 +#define SYS_pipe2 59 +#define SYS_quotactl 60 +#define SYS_getdents64 61 +#define SYS_lseek 62 +#define SYS_read 63 +#define SYS_write 64 +#define SYS_readv 65 +#define SYS_writev 66 +#define SYS_pread64 67 +#define SYS_pwrite64 68 +#define SYS_preadv 69 +#define SYS_pwritev 70 +#define SYS_sendfile 71 +#define SYS_pselect6 72 +#define SYS_ppoll 73 +#define SYS_signalfd4 74 +#define SYS_vmsplice 75 +#define SYS_splice 76 +#define SYS_tee 77 +#define SYS_readlinkat 78 +#define SYS_fstatat 79 +#define SYS_fstat 80 +#define SYS_sync 81 +#define SYS_fsync 82 +#define SYS_fdatasync 83 +#define SYS_sync_file_range 84 +#define SYS_timerfd_create 85 +#define SYS_timerfd_settime 86 +#define SYS_timerfd_gettime 87 +#define SYS_utimensat 88 +#define SYS_acct 89 +#define SYS_capget 90 +#define SYS_capset 91 +#define SYS_personality 92 +#define SYS_exit 93 +#define SYS_exit_group 94 +#define SYS_waitid 95 +#define SYS_set_tid_address 96 +#define SYS_unshare 97 +#define SYS_futex 98 +#define SYS_set_robust_list 99 +#define SYS_get_robust_list 100 +#define SYS_nanosleep 101 +#define SYS_getitimer 102 +#define SYS_setitimer 103 +#define SYS_kexec_load 104 +#define SYS_init_module 105 +#define SYS_delete_module 106 +#define SYS_timer_create 107 +#define SYS_timer_gettime 108 +#define SYS_timer_getoverrun 109 +#define SYS_timer_settime 110 +#define SYS_timer_delete 111 +#define SYS_clock_settime 112 +#define SYS_clock_gettime 113 +#define SYS_clock_getres 114 +#define SYS_clock_nanosleep 115 +#define SYS_syslog 116 +#define SYS_ptrace 117 +#define SYS_sched_setparam 118 +#define SYS_sched_setscheduler 119 +#define SYS_sched_getscheduler 120 +#define SYS_sched_getparam 121 +#define SYS_sched_setaffinity 122 +#define SYS_sched_getaffinity 123 +#define SYS_sched_yield 124 +#define SYS_sched_get_priority_max 125 +#define SYS_sched_get_priority_min 126 +#define SYS_sched_rr_get_interval 127 +#define SYS_restart_syscall 128 +#define SYS_kill 129 +#define SYS_tkill 130 +#define SYS_tgkill 131 +#define SYS_sigaltstack 132 +#define SYS_rt_sigsuspend 133 +#define SYS_rt_sigaction 134 +#define SYS_rt_sigprocmask 135 +#define SYS_rt_sigpending 136 +#define SYS_rt_sigtimedwait 137 +#define SYS_rt_sigqueueinfo 138 +#define SYS_rt_sigreturn 139 +#define SYS_setpriority 140 +#define SYS_getpriority 141 +#define SYS_reboot 142 +#define SYS_setregid 143 +#define SYS_setgid 144 +#define SYS_setreuid 145 +#define SYS_setuid 146 +#define SYS_setresuid 147 +#define SYS_getresuid 148 +#define SYS_setresgid 149 +#define SYS_getresgid 150 +#define SYS_setfsuid 151 +#define SYS_setfsgid 152 +#define SYS_times 153 +#define SYS_setpgid 154 +#define SYS_getpgid 155 +#define SYS_getsid 156 +#define SYS_setsid 157 +#define SYS_getgroups 158 +#define SYS_setgroups 159 +#define SYS_uname 160 +#define SYS_sethostname 161 +#define SYS_setdomainname 162 +#define SYS_getrlimit 163 +#define SYS_setrlimit 164 +#define SYS_getrusage 165 +#define SYS_umask 166 +#define SYS_prctl 167 +#define SYS_getcpu 168 +#define SYS_gettimeofday 169 +#define SYS_settimeofday 170 +#define SYS_adjtimex 171 +#define SYS_getpid 172 +#define SYS_getppid 173 +#define SYS_getuid 174 +#define SYS_geteuid 175 +#define SYS_getgid 176 +#define SYS_getegid 177 +#define SYS_gettid 178 +#define SYS_sysinfo 179 +#define SYS_mq_open 180 +#define SYS_mq_unlink 181 +#define SYS_mq_timedsend 182 +#define SYS_mq_timedreceive 183 +#define SYS_mq_notify 184 +#define SYS_mq_getsetattr 185 +#define SYS_msgget 186 +#define SYS_msgctl 187 +#define SYS_msgrcv 188 +#define SYS_msgsnd 189 +#define SYS_semget 190 +#define SYS_semctl 191 +#define SYS_semtimedop 192 +#define SYS_semop 193 +#define SYS_shmget 194 +#define SYS_shmctl 195 +#define SYS_shmat 196 +#define SYS_shmdt 197 +#define SYS_socket 198 +#define SYS_socketpair 199 +#define SYS_bind 200 +#define SYS_listen 201 +#define SYS_accept 202 +#define SYS_connect 203 +#define SYS_getsockname 204 +#define SYS_getpeername 205 +#define SYS_sendto 206 +#define SYS_recvfrom 207 +#define SYS_setsockopt 208 +#define SYS_getsockopt 209 +#define SYS_shutdown 210 +#define SYS_sendmsg 211 +#define SYS_recvmsg 212 +#define SYS_readahead 213 +#define SYS_brk 214 +#define SYS_munmap 215 +#define SYS_mremap 216 +#define SYS_add_key 217 +#define SYS_request_key 218 +#define SYS_keyctl 219 +#define SYS_clone 220 +#define SYS_execve 221 +#define SYS_mmap 222 +#define SYS_fadvise64 223 +#define SYS_swapon 224 +#define SYS_swapoff 225 +#define SYS_mprotect 226 +#define SYS_msync 227 +#define SYS_mlock 228 +#define SYS_munlock 229 +#define SYS_mlockall 230 +#define SYS_munlockall 231 +#define SYS_mincore 232 +#define SYS_madvise 233 +#define SYS_remap_file_pages 234 +#define SYS_mbind 235 +#define SYS_get_mempolicy 236 +#define SYS_set_mempolicy 237 +#define SYS_migrate_pages 238 +#define SYS_move_pages 239 +#define SYS_rt_tgsigqueueinfo 240 +#define SYS_perf_event_open 241 +#define SYS_accept4 242 +#define SYS_recvmmsg 243 +#define SYS_arch_specific_syscall 244 +#define SYS_wait4 260 +#define SYS_prlimit64 261 +#define SYS_fanotify_init 262 +#define SYS_fanotify_mark 263 +#define SYS_name_to_handle_at 264 +#define SYS_open_by_handle_at 265 +#define SYS_clock_adjtime 266 +#define SYS_syncfs 267 +#define SYS_setns 268 +#define SYS_sendmmsg 269 +#define SYS_process_vm_readv 270 +#define SYS_process_vm_writev 271 +#define SYS_kcmp 272 +#define SYS_finit_module 273 +#define SYS_sched_setattr 274 +#define SYS_sched_getattr 275 +#define SYS_renameat2 276 +#define SYS_seccomp 277 +#define SYS_getrandom 278 +#define SYS_memfd_create 279 +#define SYS_bpf 280 +#define SYS_execveat 281 +#define SYS_userfaultfd 282 +#define SYS_membarrier 283 +#define SYS_mlock2 284 +#define SYS_copy_file_range 285 +#define SYS_preadv2 286 +#define SYS_pwritev2 287 +#define SYS_pkey_mprotect 288 +#define SYS_pkey_alloc 289 +#define SYS_pkey_free 290 +#define SYS_statx 291 +#define SYS_io_pgetevents 292 +#define SYS_rseq 293 +#define SYS_kexec_file_load 294 +/* +* LAB1: you may need to define SYS_task_info here +*/ +#define SYS_pidfd_send_signal 424 +#define SYS_io_uring_setup 425 +#define SYS_io_uring_enter 426 +#define SYS_io_uring_register 427 +#define SYS_open_tree 428 +#define SYS_move_mount 429 +#define SYS_fsopen 430 +#define SYS_fsconfig 431 +#define SYS_fsmount 432 +#define SYS_fspick 433 +#define SYS_pidfd_open 434 +#define SYS_clone3 435 +#define SYS_openat2 437 +#define SYS_pidfd_getfd 438 +#define SYS_faccessat2 439 +#define SYS_riscv_flush_icache 244 + 15 diff --git a/os/timer.c b/os/timer.c new file mode 100644 index 0000000..6069e94 --- /dev/null +++ b/os/timer.c @@ -0,0 +1,24 @@ +#include "timer.h" +#include "riscv.h" +#include "sbi.h" + +/// read the `mtime` regiser +uint64 get_cycle() +{ + return r_time(); +} + +/// Enable timer interrupt +void timer_init() +{ + // Enable supervisor timer interrupt + w_sie(r_sie() | SIE_STIE); + set_next_timer(); +} + +/// Set the next timer interrupt +void set_next_timer() +{ + const uint64 timebase = CPU_FREQ / TICKS_PER_SEC; + set_timer(get_cycle() + timebase); +} \ No newline at end of file diff --git a/os/timer.h b/os/timer.h new file mode 100644 index 0000000..217da4f --- /dev/null +++ b/os/timer.h @@ -0,0 +1,19 @@ +#ifndef TIMER_H +#define TIMER_H + +#include "types.h" + +#define TICKS_PER_SEC (100) +// QEMU +#define CPU_FREQ (12500000) + +uint64 get_cycle(); +void timer_init(); +void set_next_timer(); + +typedef struct { // How to process structures in VA? + uint64 sec; // 自 Unix 纪元起的秒数 + uint64 usec; // 微秒数 +} TimeVal; + +#endif // TIMER_H \ No newline at end of file diff --git a/os/trampoline.S b/os/trampoline.S new file mode 100644 index 0000000..7af374b --- /dev/null +++ b/os/trampoline.S @@ -0,0 +1,129 @@ + # + # code to switch between user and kernel space. + # + # this code is mapped at the same virtual address + # (TRAMPOLINE) in user and kernel space so that + # it continues to work when it switches page tables. + # + # kernel.ld causes this to be aligned + # to a page boundary. + # + .section trampsec +.globl trampoline +trampoline: +.align 4 +.globl uservec +uservec: + # + # trap.c sets stvec to point here, so + # traps from user space start here, + # in supervisor mode, but with a + # user page table. + # + # sscratch points to where the process's p->trapframe is + # mapped into user space, at TRAPFRAME. + # + + # swap a0 and sscratch + # so that a0 is TRAPFRAME + csrrw a0, sscratch, a0 + + # save the user registers in TRAPFRAME + sd ra, 40(a0) + sd sp, 48(a0) + sd gp, 56(a0) + sd tp, 64(a0) + sd t0, 72(a0) + sd t1, 80(a0) + sd t2, 88(a0) + sd s0, 96(a0) + sd s1, 104(a0) + sd a1, 120(a0) + sd a2, 128(a0) + sd a3, 136(a0) + sd a4, 144(a0) + sd a5, 152(a0) + sd a6, 160(a0) + sd a7, 168(a0) + sd s2, 176(a0) + sd s3, 184(a0) + sd s4, 192(a0) + sd s5, 200(a0) + sd s6, 208(a0) + sd s7, 216(a0) + sd s8, 224(a0) + sd s9, 232(a0) + sd s10, 240(a0) + sd s11, 248(a0) + sd t3, 256(a0) + sd t4, 264(a0) + sd t5, 272(a0) + sd t6, 280(a0) + + csrr t0, sscratch + sd t0, 112(a0) + csrr t1, sepc + sd t1, 24(a0) + ld sp, 8(a0) + ld tp, 32(a0) + ld t0, 16(a0) + ld t1, 0(a0) + csrw satp, t1 + sfence.vma zero, zero + jr t0 + +.globl userret +userret: + # userret(TRAPFRAME, pagetable) + # switch from kernel to user. + # usertrapret() calls here. + # a0: TRAPFRAME, in user page table. + # a1: user page table, for satp. + + # switch to the user page table. + csrw satp, a1 + sfence.vma zero, zero + + # put the saved user a0 in sscratch, so we + # can swap it with our a0 (TRAPFRAME) in the last step. + ld t0, 112(a0) + csrw sscratch, t0 + + # restore all but a0 from TRAPFRAME + ld ra, 40(a0) + ld sp, 48(a0) + ld gp, 56(a0) + ld tp, 64(a0) + ld t0, 72(a0) + ld t1, 80(a0) + ld t2, 88(a0) + ld s0, 96(a0) + ld s1, 104(a0) + ld a1, 120(a0) + ld a2, 128(a0) + ld a3, 136(a0) + ld a4, 144(a0) + ld a5, 152(a0) + ld a6, 160(a0) + ld a7, 168(a0) + ld s2, 176(a0) + ld s3, 184(a0) + ld s4, 192(a0) + ld s5, 200(a0) + ld s6, 208(a0) + ld s7, 216(a0) + ld s8, 224(a0) + ld s9, 232(a0) + ld s10, 240(a0) + ld s11, 248(a0) + ld t3, 256(a0) + ld t4, 264(a0) + ld t5, 272(a0) + ld t6, 280(a0) + + # restore user a0, and save TRAPFRAME in sscratch + csrrw a0, sscratch, a0 + + # return to user mode and user pc. + # usertrapret() set up sstatus and sepc. + sret diff --git a/os/trap.c b/os/trap.c new file mode 100644 index 0000000..173f944 --- /dev/null +++ b/os/trap.c @@ -0,0 +1,123 @@ +#include "trap.h" +#include "defs.h" +#include "loader.h" +#include "syscall.h" +#include "timer.h" + +extern char trampoline[], uservec[]; +extern char userret[]; + +void kerneltrap() +{ + if ((r_sstatus() & SSTATUS_SPP) == 0) + panic("kerneltrap: not from supervisor mode"); + panic("trap from kerne"); +} + +// set up to take exceptions and traps while in the kernel. +void set_usertrap(void) +{ + w_stvec(((uint64)TRAMPOLINE + (uservec - trampoline)) & ~0x3); // DIRECT +} + +void set_kerneltrap(void) +{ + w_stvec((uint64)kerneltrap & ~0x3); // DIRECT +} + +// set up to take exceptions and traps while in the kernel. +void trap_init(void) +{ + // intr_on(); + set_kerneltrap(); +} + +void unknown_trap() +{ + errorf("unknown trap: %p, stval = %p", r_scause(), r_stval()); + exit(-1); +} + +// +// handle an interrupt, exception, or system call from user space. +// called from trampoline.S +// +void usertrap() +{ + set_kerneltrap(); + struct trapframe *trapframe = curr_proc()->trapframe; + tracef("trap from user epc = %p", trapframe->epc); + if ((r_sstatus() & SSTATUS_SPP) != 0) + panic("usertrap: not from user mode"); + + uint64 cause = r_scause(); + if (cause & (1ULL << 63)) { + cause &= ~(1ULL << 63); + switch (cause) { + case SupervisorTimer: + tracef("time interrupt!"); + set_next_timer(); + yield(); + break; + default: + unknown_trap(); + break; + } + } else { + switch (cause) { + case UserEnvCall: + trapframe->epc += 4; + syscall(); + break; + case StoreMisaligned: + case StorePageFault: + case InstructionMisaligned: + case InstructionPageFault: + case LoadMisaligned: + case LoadPageFault: + errorf("%d in application, bad addr = %p, bad instruction = %p, " + "core dumped.", + cause, r_stval(), trapframe->epc); + exit(-2); + break; + case IllegalInstruction: + errorf("IllegalInstruction in application, core dumped."); + exit(-3); + break; + default: + unknown_trap(); + break; + } + } + usertrapret(); +} + +// +// return to user space +// +void usertrapret() +{ + set_usertrap(); + struct trapframe *trapframe = curr_proc()->trapframe; + trapframe->kernel_satp = r_satp(); // kernel page table + trapframe->kernel_sp = + curr_proc()->kstack + KSTACK_SIZE; // process's kernel stack + trapframe->kernel_trap = (uint64)usertrap; + trapframe->kernel_hartid = r_tp(); // unuesd + + w_sepc(trapframe->epc); + // set up the registers that trampoline.S's sret will use + // to get to user space. + + // set S Previous Privilege mode to User. + uint64 x = r_sstatus(); + x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode + x |= SSTATUS_SPIE; // enable interrupts in user mode + w_sstatus(x); + + // tell trampoline.S the user page table to switch to. + uint64 satp = MAKE_SATP(curr_proc()->pagetable); + uint64 fn = TRAMPOLINE + (userret - trampoline); + tracef("return to user @ %p", trapframe->epc); + ((void (*)(uint64, uint64))fn)(TRAPFRAME, satp); +} \ No newline at end of file diff --git a/os/trap.h b/os/trap.h new file mode 100644 index 0000000..1ecfd28 --- /dev/null +++ b/os/trap.h @@ -0,0 +1,74 @@ +#ifndef TRAP_H +#define TRAP_H + +#include "types.h" + +struct trapframe { + /* 0 */ uint64 kernel_satp; // kernel page table + /* 8 */ uint64 kernel_sp; // top of process's kernel stack + /* 16 */ uint64 kernel_trap; // usertrap() + /* 24 */ uint64 epc; // saved user program counter + /* 32 */ uint64 kernel_hartid; // saved kernel tp + /* 40 */ uint64 ra; + /* 48 */ uint64 sp; + /* 56 */ uint64 gp; + /* 64 */ uint64 tp; + /* 72 */ uint64 t0; + /* 80 */ uint64 t1; + /* 88 */ uint64 t2; + /* 96 */ uint64 s0; + /* 104 */ uint64 s1; + /* 112 */ uint64 a0; + /* 120 */ uint64 a1; + /* 128 */ uint64 a2; + /* 136 */ uint64 a3; + /* 144 */ uint64 a4; + /* 152 */ uint64 a5; + /* 160 */ uint64 a6; + /* 168 */ uint64 a7; + /* 176 */ uint64 s2; + /* 184 */ uint64 s3; + /* 192 */ uint64 s4; + /* 200 */ uint64 s5; + /* 208 */ uint64 s6; + /* 216 */ uint64 s7; + /* 224 */ uint64 s8; + /* 232 */ uint64 s9; + /* 240 */ uint64 s10; + /* 248 */ uint64 s11; + /* 256 */ uint64 t3; + /* 264 */ uint64 t4; + /* 272 */ uint64 t5; + /* 280 */ uint64 t6; +}; + +enum Exception { + InstructionMisaligned = 0, + InstructionAccessFault = 1, + IllegalInstruction = 2, + Breakpoint = 3, + LoadMisaligned = 4, + LoadAccessFault = 5, + StoreMisaligned = 6, + StoreAccessFault = 7, + UserEnvCall = 8, + SupervisorEnvCall = 9, + MachineEnvCall = 11, + InstructionPageFault = 12, + LoadPageFault = 13, + StorePageFault = 15, +}; + +enum Interrupt { + UserSoft = 0, + SupervisorSoft, + UserTimer = 4, + SupervisorTimer, + UserExternal = 8, + SupervisorExternal, +}; + +void trap_init(); +void usertrapret(); + +#endif // TRAP_H \ No newline at end of file diff --git a/os/types.h b/os/types.h new file mode 100644 index 0000000..8866144 --- /dev/null +++ b/os/types.h @@ -0,0 +1,12 @@ +#ifndef TYPES_H +#define TYPES_H + +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long uint64; + +#endif // TYPES_H \ No newline at end of file diff --git a/os/vm.c b/os/vm.c new file mode 100644 index 0000000..c5df5b6 --- /dev/null +++ b/os/vm.c @@ -0,0 +1,297 @@ +#include "vm.h" +#include "defs.h" +#include "riscv.h" + +pagetable_t kernel_pagetable; + +extern char e_text[]; // kernel.ld sets this to end of kernel code. +extern char trampoline[]; + +// Make a direct-map page table for the kernel. +pagetable_t kvmmake(void) +{ + pagetable_t kpgtbl; + kpgtbl = (pagetable_t)kalloc(); + memset(kpgtbl, 0, PGSIZE); + // map kernel text executable and read-only. + kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)e_text - KERNBASE, + PTE_R | PTE_X); + // map kernel data and the physical RAM we'll make use of. + kvmmap(kpgtbl, (uint64)e_text, (uint64)e_text, PHYSTOP - (uint64)e_text, + PTE_R | PTE_W); + kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); + return kpgtbl; +} + +// Initialize the one kernel_pagetable +// Switch h/w page table register to the kernel's page table, +// and enable paging. +void kvm_init(void) +{ + kernel_pagetable = kvmmake(); + w_satp(MAKE_SATP(kernel_pagetable)); + sfence_vma(); + infof("enable pageing at %p", r_satp()); +} + +// Return the address of the PTE in page table pagetable +// that corresponds to virtual address va. If alloc!=0, +// create any required page-table pages. +// +// The risc-v Sv39 scheme has three levels of page-table +// pages. A page-table page contains 512 64-bit PTEs. +// A 64-bit virtual address is split into five fields: +// 39..63 -- must be zero. +// 30..38 -- 9 bits of level-2 index. +// 21..29 -- 9 bits of level-1 index. +// 12..20 -- 9 bits of level-0 index. +// 0..11 -- 12 bits of byte offset within the page. +pte_t *walk(pagetable_t pagetable, uint64 va, int alloc) +{ + if (va >= MAXVA) + panic("walk"); + + for (int level = 2; level > 0; level--) { + pte_t *pte = &pagetable[PX(level, va)]; + if (*pte & PTE_V) { + pagetable = (pagetable_t)PTE2PA(*pte); + } else { + if (!alloc || (pagetable = (pde_t *)kalloc()) == 0) + return 0; + memset(pagetable, 0, PGSIZE); + *pte = PA2PTE(pagetable) | PTE_V; + } + } + return &pagetable[PX(0, va)]; +} + +// Look up a virtual address, return the physical address, +// or 0 if not mapped. +// Can only be used to look up user pages. +uint64 walkaddr(pagetable_t pagetable, uint64 va) +{ + pte_t *pte; + uint64 pa; + + if (va >= MAXVA) + return 0; + + pte = walk(pagetable, va, 0); + if (pte == 0) + return 0; + if ((*pte & PTE_V) == 0) + return 0; + if ((*pte & PTE_U) == 0) + return 0; + pa = PTE2PA(*pte); + return pa; +} + +// Look up a virtual address, return the physical address, +uint64 useraddr(pagetable_t pagetable, uint64 va) +{ + uint64 page = walkaddr(pagetable, va); + if (page == 0) + return 0; + return page | (va & 0xFFFULL); +} + +// Add a mapping to the kernel page table. +// only used when booting. +// does not flush TLB or enable paging. +void kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm) +{ + if (mappages(kpgtbl, va, sz, pa, perm) != 0) + panic("kvmmap"); +} + +// Create PTEs for virtual addresses starting at va that refer to +// physical addresses starting at pa. va and size might not +// be page-aligned. Returns 0 on success, -1 if walk() couldn't +// allocate a needed page-table page. +int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) +{ + uint64 a, last; + pte_t *pte; + + a = PGROUNDDOWN(va); + last = PGROUNDDOWN(va + size - 1); + for (;;) { + if ((pte = walk(pagetable, a, 1)) == 0) + return -1; + if (*pte & PTE_V) { + errorf("remap"); + return -1; + } + *pte = PA2PTE(pa) | perm | PTE_V; + if (a == last) + break; + a += PGSIZE; + pa += PGSIZE; + } + return 0; +} + +// Remove npages of mappings starting from va. va must be +// page-aligned. The mappings must exist. +// Optionally free the physical memory. +void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) +{ + uint64 a; + pte_t *pte; + + if ((va % PGSIZE) != 0) + panic("uvmunmap: not aligned"); + + for (a = va; a < va + npages * PGSIZE; a += PGSIZE) { + if ((pte = walk(pagetable, a, 0)) == 0) + continue; + if ((*pte & PTE_V) != 0) { + if (PTE_FLAGS(*pte) == PTE_V) + panic("uvmunmap: not a leaf"); + if (do_free) { + uint64 pa = PTE2PA(*pte); + kfree((void *)pa); + } + } + *pte = 0; + } +} + +// create an empty user page table. +// returns 0 if out of memory. +pagetable_t uvmcreate() +{ + pagetable_t pagetable; + pagetable = (pagetable_t)kalloc(); + if (pagetable == 0) { + errorf("uvmcreate: kalloc error"); + return 0; + } + memset(pagetable, 0, PGSIZE); + if (mappages(pagetable, TRAMPOLINE, PAGE_SIZE, (uint64)trampoline, + PTE_R | PTE_X) < 0) { + kfree(pagetable); + errorf("uvmcreate: mappages error"); + return 0; + } + return pagetable; +} + +// Recursively free page-table pages. +// All leaf mappings must already have been removed. +void freewalk(pagetable_t pagetable) +{ + // there are 2^9 = 512 PTEs in a page table. + for (int i = 0; i < 512; i++) { + pte_t pte = pagetable[i]; + if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0) { + // this PTE points to a lower-level page table. + uint64 child = PTE2PA(pte); + freewalk((pagetable_t)child); + pagetable[i] = 0; + } else if (pte & PTE_V) { + panic("freewalk: leaf"); + } + } + kfree((void *)pagetable); +} + +/** + * @brief Free user memory pages, then free page-table pages. + * + * @param max_page The max vaddr of user-space. + */ +void uvmfree(pagetable_t pagetable, uint64 max_page) +{ + if (max_page > 0) + uvmunmap(pagetable, 0, max_page, 1); + freewalk(pagetable); +} + +// Copy from kernel to user. +// Copy len bytes from src to virtual address dstva in a given page table. +// Return 0 on success, -1 on error. +int copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) +{ + uint64 n, va0, pa0; + + while (len > 0) { + va0 = PGROUNDDOWN(dstva); + pa0 = walkaddr(pagetable, va0); + if (pa0 == 0) + return -1; + n = PGSIZE - (dstva - va0); + if (n > len) + n = len; + memmove((void *)(pa0 + (dstva - va0)), src, n); + + len -= n; + src += n; + dstva = va0 + PGSIZE; + } + return 0; +} + +// Copy from user to kernel. +// Copy len bytes to dst from virtual address srcva in a given page table. +// Return 0 on success, -1 on error. +int copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) +{ + uint64 n, va0, pa0; + + while (len > 0) { + va0 = PGROUNDDOWN(srcva); + pa0 = walkaddr(pagetable, va0); + if (pa0 == 0) + return -1; + n = PGSIZE - (srcva - va0); + if (n > len) + n = len; + memmove(dst, (void *)(pa0 + (srcva - va0)), n); + + len -= n; + dst += n; + srcva = va0 + PGSIZE; + } + return 0; +} + +// Copy a null-terminated string from user to kernel. +// Copy bytes to dst from virtual address srcva in a given page table, +// until a '\0', or max. +// Return 0 on success, -1 on error. +int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) +{ + uint64 n, va0, pa0; + int got_null = 0, len = 0; + + while (got_null == 0 && max > 0) { + va0 = PGROUNDDOWN(srcva); + pa0 = walkaddr(pagetable, va0); + if (pa0 == 0) + return -1; + n = PGSIZE - (srcva - va0); + if (n > max) + n = max; + + char *p = (char *)(pa0 + (srcva - va0)); + while (n > 0) { + if (*p == '\0') { + *dst = '\0'; + got_null = 1; + break; + } else { + *dst = *p; + } + --n; + --max; + p++; + dst++; + len++; + } + + srcva = va0 + PGSIZE; + } + return len; +} diff --git a/os/vm.h b/os/vm.h new file mode 100644 index 0000000..05622ac --- /dev/null +++ b/os/vm.h @@ -0,0 +1,19 @@ +#ifndef VM_H +#define VM_H + +#include "riscv.h" +#include "types.h" + +void kvm_init(void); +void kvmmap(pagetable_t, uint64, uint64, uint64, int); +int mappages(pagetable_t, uint64, uint64, uint64, int); +pagetable_t uvmcreate(void); +void uvmfree(pagetable_t, uint64); +void uvmunmap(pagetable_t, uint64, uint64, int); +uint64 walkaddr(pagetable_t, uint64); +uint64 useraddr(pagetable_t, uint64); +int copyout(pagetable_t, uint64, char *, uint64); +int copyin(pagetable_t, char *, uint64, uint64); +int copyinstr(pagetable_t, char *, uint64, uint64); + +#endif // VM_H \ No newline at end of file diff --git a/scripts/kernelld.py b/scripts/kernelld.py new file mode 100644 index 0000000..9561a8f --- /dev/null +++ b/scripts/kernelld.py @@ -0,0 +1,69 @@ +import os + +TARGET_DIR = "./user/target/bin/" + +if __name__ == '__main__': + f = open("os/kernel_app.ld", mode="w") + apps = os.listdir(TARGET_DIR) + f.write( +'''OUTPUT_ARCH(riscv) +ENTRY(_entry) +BASE_ADDRESS = 0x80200000; + +SECTIONS +{ + . = BASE_ADDRESS; + skernel = .; + + s_text = .; + .text : { + *(.text.entry) + *(.text .text.*) + . = ALIGN(0x1000); + *(trampsec) + . = ALIGN(0x1000); + } + + . = ALIGN(4K); + e_text = .; + s_rodata = .; + .rodata : { + *(.rodata .rodata.*) + } + + . = ALIGN(4K); + e_rodata = .; + s_data = .; + .data : { + *(.data) +''') + for (idx, _) in enumerate(apps): + f.write(' . = ALIGN(0x1000);\n') + f.write(' *(.data.app{})\n'.format(idx)) + f.write( +''' + . = ALIGN(0x1000); + *(.data.*) + *(.sdata .sdata.*) + } + + . = ALIGN(4K); + e_data = .; + .bss : { + *(.bss.stack) + s_bss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + } + + . = ALIGN(4K); + e_bss = .; + ekernel = .; + + /DISCARD/ : { + *(.eh_frame) + } +} +''') + f.close() + diff --git a/scripts/pack.py b/scripts/pack.py new file mode 100644 index 0000000..c041b55 --- /dev/null +++ b/scripts/pack.py @@ -0,0 +1,41 @@ +import os + +TARGET_DIR = "./user/target/bin/" + +if __name__ == '__main__': + f = open("os/link_app.S", mode="w") + apps = os.listdir(TARGET_DIR) + apps.sort() + f.write( +''' .align 4 + .section .data + .global _app_num +_app_num: + .quad {} +'''.format(len(apps)) + ) + + for (idx, _) in enumerate(apps): + f.write(' .quad app_{}_start\n'.format(idx)) + f.write(' .quad app_{}_end\n'.format(len(apps) - 1)) + + f.write( +''' + .global _app_names +_app_names: +'''); + + for app in apps: + f.write(" .string \"" + app + "\"\n") + + for (idx, app) in enumerate(apps): + f.write( +''' + .section .data.app{0} + .global app_{0}_start +app_{0}_start: + .incbin "{1}" +'''.format(idx, TARGET_DIR + app) + ) + f.write('app_{}_end:\n\n'.format(len(apps) - 1)) + f.close() \ No newline at end of file