From 8645ae06c12bb0d4813537efe74814047e517497 Mon Sep 17 00:00:00 2001 From: rcy17 Date: Wed, 9 Mar 2022 20:38:26 +0800 Subject: [PATCH 1/7] ch1 --- .clang-format | 561 ++++++++++++++++++++++++++++++++++++ .gdbinit | 7 + .gitignore | 12 + Makefile | 107 +++++++ bootloader/rustsbi-qemu.bin | Bin 0 -> 103288 bytes os/console.c | 12 + os/console.h | 7 + os/defs.h | 13 + os/entry.S | 12 + os/kernel.ld | 51 ++++ os/log.h | 120 ++++++++ os/main.c | 40 +++ os/printf.c | 83 ++++++ os/printf.h | 4 + os/riscv.h | 322 +++++++++++++++++++++ os/sbi.c | 39 +++ os/sbi.h | 8 + os/types.h | 12 + 18 files changed, 1410 insertions(+) create mode 100644 .clang-format create mode 100644 .gdbinit create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 bootloader/rustsbi-qemu.bin create mode 100644 os/console.c create mode 100644 os/console.h create mode 100644 os/defs.h create mode 100644 os/entry.S create mode 100644 os/kernel.ld create mode 100644 os/log.h create mode 100644 os/main.c create mode 100644 os/printf.c create mode 100644 os/printf.h create mode 100644 os/riscv.h create mode 100644 os/sbi.c create mode 100644 os/sbi.h create mode 100644 os/types.h 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..6828826 --- /dev/null +++ b/Makefile @@ -0,0 +1,107 @@ +.PHONY: clean build user +all: build_kernel + +LOG ?= error + +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 +MKDIR_P = mkdir -p + +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))) + +-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) + +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 $@.$$$$ + +build: build/kernel + +build/kernel: $(OBJS) + $(LD) $(LDFLAGS) -T os/kernel.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) + +# 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) diff --git a/bootloader/rustsbi-qemu.bin b/bootloader/rustsbi-qemu.bin new file mode 100644 index 0000000000000000000000000000000000000000..5ccab433b2cac6c11e47ae827904839c4275dfa7 GIT binary patch literal 103288 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 +#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..c162e50 --- /dev/null +++ b/os/printf.h @@ -0,0 +1,4 @@ +#ifndef PRINTF_H +#define PRINTF_H +void printf(char *, ...); +#endif // PRINTF_H diff --git a/os/riscv.h b/os/riscv.h new file mode 100644 index 0000000..ba2fdbf --- /dev/null +++ b/os/riscv.h @@ -0,0 +1,322 @@ +#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 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 *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..31e9e43 --- /dev/null +++ b/os/sbi.c @@ -0,0 +1,39 @@ +#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); +} \ No newline at end of file diff --git a/os/sbi.h b/os/sbi.h new file mode 100644 index 0000000..8e98c2a --- /dev/null +++ b/os/sbi.h @@ -0,0 +1,8 @@ +#ifndef SBI_H +#define SBI_H + +void console_putchar(int); +int console_getchar(); +void shutdown(); + +#endif // SBI_H 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 From 235e2ca5f5a0f6f68b7528bd5e26a60a1b78ab1c Mon Sep 17 00:00:00 2001 From: rcy17 Date: Wed, 9 Mar 2022 20:39:08 +0800 Subject: [PATCH 2/7] ch2 --- Makefile | 28 ++++- os/const.h | 12 ++ os/defs.h | 6 +- os/kernelld.py | 68 ++++++++++ os/loader.c | 46 +++++++ os/loader.h | 15 +++ os/log.h | 14 +-- os/main.c | 31 ++--- os/pack.py | 42 +++++++ os/printf.h | 2 + os/string.c | 100 +++++++++++++++ os/string.h | 14 +++ os/syscall.c | 50 ++++++++ os/syscall.h | 6 + os/syscall_ids.h | 295 ++++++++++++++++++++++++++++++++++++++++++++ os/trampoline.S | 132 ++++++++++++++++++++ os/trap.c | 79 ++++++++++++ os/trap.h | 74 +++++++++++ scripts/kernelld.py | 69 +++++++++++ scripts/pack.py | 41 ++++++ 20 files changed, 1087 insertions(+), 37 deletions(-) create mode 100644 os/const.h create mode 100644 os/kernelld.py create mode 100644 os/loader.c create mode 100644 os/loader.h create mode 100644 os/pack.py create mode 100644 os/string.c create mode 100644 os/string.h create mode 100644 os/syscall.c create mode 100644 os/syscall.h create mode 100644 os/syscall_ids.h create mode 100644 os/trampoline.S create mode 100644 os/trap.c create mode 100644 os/trap.h create mode 100644 scripts/kernelld.py create mode 100644 scripts/pack.py diff --git a/Makefile b/Makefile index 6828826..37e4f16 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ all: build_kernel LOG ?= error K = os +U = user TOOLPREFIX = riscv64-unknown-elf- CC = $(TOOLPREFIX)gcc @@ -14,8 +15,6 @@ OBJDUMP = $(TOOLPREFIX)objdump PY = python3 GDB = $(TOOLPREFIX)gdb CP = cp -MKDIR_P = mkdir -p - BUILDDIR = build C_SRCS = $(wildcard $K/*.c) AS_SRCS = $(wildcard $K/*.S) @@ -25,6 +24,10 @@ 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 @@ -70,16 +73,23 @@ $(HEADER_DEP): $(BUILDDIR)/$K/%.d : $K/%.c 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) - $(LD) $(LDFLAGS) -T os/kernel.ld -o $(BUILDDIR)/kernel $(OBJS) +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) + rm -rf $(BUILDDIR) os/kernel_app.ld os/link_app.S + make -C $(U) clean # BOARD BOARD ?= qemu @@ -105,3 +115,11 @@ 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]') + +user: + make -C $(U) CHAPTER=$(CHAPTER) BASE=$(BASE) + +test: user run + diff --git a/os/const.h b/os/const.h new file mode 100644 index 0000000..fa56854 --- /dev/null +++ b/os/const.h @@ -0,0 +1,12 @@ +#ifndef CONST_H +#define CONST_H + +#define PAGE_SIZE (0x1000) + +enum { + STDIN = 0, + STDOUT = 1, + STDERR = 2, +}; + +#endif // CONST_H \ No newline at end of file diff --git a/os/defs.h b/os/defs.h index 1ecb3e9..2d66039 100644 --- a/os/defs.h +++ b/os/defs.h @@ -1,13 +1,17 @@ #ifndef DEFS_H #define DEFS_H +#include "const.h" #include "log.h" #include "printf.h" #include "riscv.h" #include "sbi.h" +#include "string.h" #include "types.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 \ No newline at end of file +#endif // DEF_H 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..6fc7c6b --- /dev/null +++ b/os/loader.c @@ -0,0 +1,46 @@ +#include "loader.h" +#include "defs.h" +#include "trap.h" + +static int app_cur, app_num; +static uint64 *app_info_ptr; +extern char _app_num[], userret[], boot_stack_top[], ekernel[]; + +void loader_init() +{ + if ((uint64)ekernel >= BASE_ADDRESS) { + panic("kernel too large...\n"); + } + app_info_ptr = (uint64 *)_app_num; + app_cur = -1; + app_num = *app_info_ptr; +} + +__attribute__((aligned(4096))) char user_stack[USER_STACK_SIZE]; +__attribute__((aligned(4096))) char trap_page[TRAP_PAGE_SIZE]; + +int load_app(uint64 *info) +{ + uint64 start = info[0], end = info[1], length = end - start; + memset((void *)BASE_ADDRESS, 0, MAX_APP_SIZE); + memmove((void *)BASE_ADDRESS, (void *)start, length); + return length; +} + +int run_next_app() +{ + struct trapframe *trapframe = (struct trapframe *)trap_page; + app_cur++; + app_info_ptr++; + if (app_cur >= app_num) { + return -1; + } + infof("load and run app %d", app_cur); + uint64 length = load_app(app_info_ptr); + debugf("bin range = [%p, %p)", *app_info_ptr, *app_info_ptr + length); + memset(trapframe, 0, 4096); + trapframe->epc = BASE_ADDRESS; + trapframe->sp = (uint64)user_stack + USER_STACK_SIZE; + usertrapret(trapframe, (uint64)boot_stack_top); + return 0; +} \ No newline at end of file diff --git a/os/loader.h b/os/loader.h new file mode 100644 index 0000000..2448411 --- /dev/null +++ b/os/loader.h @@ -0,0 +1,15 @@ +#ifndef BATCH_H +#define BATCH_H + +#include "const.h" +#include "types.h" + +void loader_init(); +int run_next_app(); + +#define BASE_ADDRESS (0x80400000) +#define MAX_APP_SIZE (0x20000) +#define USER_STACK_SIZE PAGE_SIZE +#define TRAP_PAGE_SIZE PAGE_SIZE + +#endif // BATCH_H \ No newline at end of file diff --git a/os/log.h b/os/log.h index d027100..f88a34f 100644 --- a/os/log.h +++ b/os/log.h @@ -3,7 +3,7 @@ extern void printf(char *, ...); extern int threadid(); -extern void shutdown(); +extern void dummy(int, ...); #if defined(LOG_LEVEL_ERROR) @@ -61,7 +61,7 @@ enum LOG_COLOR { ##__VA_ARGS__); \ } while (0) #else -#define errorf(fmt, ...) +#define errorf(fmt, ...) dummy(0, ##__VA_ARGS__) #endif // USE_LOG_ERROR #if defined(USE_LOG_WARN) @@ -72,7 +72,7 @@ enum LOG_COLOR { ##__VA_ARGS__); \ } while (0) #else -#define warnf(fmt, ...) +#define warnf(fmt, ...) dummy(0, ##__VA_ARGS__) #endif // USE_LOG_WARN #if defined(USE_LOG_INFO) @@ -83,7 +83,7 @@ enum LOG_COLOR { ##__VA_ARGS__); \ } while (0) #else -#define infof(fmt, ...) +#define infof(fmt, ...) dummy(0, ##__VA_ARGS__) #endif // USE_LOG_INFO #if defined(USE_LOG_DEBUG) @@ -94,7 +94,7 @@ enum LOG_COLOR { ##__VA_ARGS__); \ } while (0) #else -#define debugf(fmt, ...) +#define debugf(fmt, ...) dummy(0, ##__VA_ARGS__) #endif // USE_LOG_DEBUG #if defined(USE_LOG_TRACE) @@ -105,7 +105,7 @@ enum LOG_COLOR { ##__VA_ARGS__); \ } while (0) #else -#define tracef(fmt, ...) +#define tracef(fmt, ...) dummy(0, ##__VA_ARGS__) #endif // USE_LOG_TRACE #define panic(fmt, ...) \ @@ -113,8 +113,6 @@ enum LOG_COLOR { 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 index 155a67a..de53bd0 100644 --- a/os/main.c +++ b/os/main.c @@ -1,14 +1,7 @@ #include "console.h" #include "defs.h" - -extern char s_text[]; -extern char e_text[]; -extern char s_rodata[]; -extern char e_rodata[]; -extern char s_data[]; -extern char e_data[]; -extern char s_bss[]; -extern char e_bss[]; +#include "loader.h" +#include "trap.h" int threadid() { @@ -17,24 +10,16 @@ int threadid() void clean_bss() { - char *p; - for (p = s_bss; p < e_bss; ++p) - *p = 0; + extern char s_bss[]; + extern char e_bss[]; + memset(s_bss, 0, e_bss - s_bss); } void main() { clean_bss(); - console_init(); - printf("\n"); printf("hello wrold!\n"); - errorf("stext: %p", s_text); - warnf("etext: %p", e_text); - infof("sroda: %p", s_rodata); - debugf("eroda: %p", e_rodata); - debugf("sdata: %p", s_data); - infof("edata: %p", e_data); - warnf("sbss : %p", s_bss); - errorf("ebss : %p", e_bss); - panic("ALL DONE"); + trap_init(); + loader_init(); + run_next_app(); } 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.h b/os/printf.h index c162e50..d9c9051 100644 --- a/os/printf.h +++ b/os/printf.h @@ -1,4 +1,6 @@ #ifndef PRINTF_H #define PRINTF_H + void printf(char *, ...); + #endif // PRINTF_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/syscall.c b/os/syscall.c new file mode 100644 index 0000000..3d495b3 --- /dev/null +++ b/os/syscall.c @@ -0,0 +1,50 @@ +#include "syscall.h" +#include "defs.h" +#include "loader.h" +#include "syscall_ids.h" +#include "trap.h" + +uint64 sys_write(int fd, char *str, uint len) +{ + debugf("sys_write fd = %d str = %x, len = %d", fd, str, len); + if (fd != STDOUT) + return -1; + for (int i = 0; i < len; ++i) { + console_putchar(str[i]); + } + return len; +} + +__attribute__((noreturn)) void sys_exit(int code) +{ + debugf("sysexit(%d)", code); + run_next_app(); + printf("ALL DONE\n"); + shutdown(); + __builtin_unreachable(); +} + +extern char trap_page[]; + +void syscall() +{ + struct trapframe *trapframe = (struct trapframe *)trap_page; + 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]); + switch (id) { + case SYS_write: + ret = sys_write(args[0], (char *)args[1], args[2]); + break; + case SYS_exit: + sys_exit(args[0]); + // __builtin_unreachable(); + 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..fd765c8 --- /dev/null +++ b/os/syscall_ids.h @@ -0,0 +1,295 @@ +#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 +#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/trampoline.S b/os/trampoline.S new file mode 100644 index 0000000..573e4c7 --- /dev/null +++ b/os/trampoline.S @@ -0,0 +1,132 @@ + # + # 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 .text +.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) + + # save the user a0 in p->trapframe->a0 + csrr t0, sscratch + sd t0, 112(a0) + + csrr t1, sepc + sd t1, 24(a0) + + ld sp, 8(a0) + ld tp, 32(a0) + ld t1, 0(a0) + # csrw satp, t1 + # sfence.vma zero, zero + ld t0, 16(a0) + 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..08b365a --- /dev/null +++ b/os/trap.c @@ -0,0 +1,79 @@ +#include "trap.h" +#include "defs.h" +#include "loader.h" +#include "syscall.h" + +extern char trampoline[], uservec[], boot_stack_top[]; +extern void *userret(uint64); + +// set up to take exceptions and traps while in the kernel. +void trap_init(void) +{ + w_stvec((uint64)uservec & ~0x3); +} + +// +// handle an interrupt, exception, or system call from user space. +// called from trampoline.S +// +void usertrap(struct trapframe *trapframe) +{ + if ((r_sstatus() & SSTATUS_SPP) != 0) + panic("usertrap: not from user mode"); + + uint64 cause = r_scause(); + if (cause == UserEnvCall) { + trapframe->epc += 4; + syscall(); + return usertrapret(trapframe, (uint64)boot_stack_top); + } + switch (cause) { + case StoreMisaligned: + case StorePageFault: + case LoadMisaligned: + case LoadPageFault: + case InstructionMisaligned: + case InstructionPageFault: + errorf("%d in application, bad addr = %p, bad instruction = %p, core " + "dumped.", + cause, r_stval(), trapframe->epc); + break; + case IllegalInstruction: + errorf("IllegalInstruction in application, epc = %p, core dumped.", + trapframe->epc); + break; + default: + errorf("unknown trap: %p, stval = %p sepc = %p", r_scause(), + r_stval(), r_sepc()); + break; + } + infof("switch to next app"); + run_next_app(); + printf("ALL DONE\n"); + shutdown(); +} + +// +// return to user space +// +void usertrapret(struct trapframe *trapframe, uint64 kstack) +{ + trapframe->kernel_satp = r_satp(); // kernel page table + trapframe->kernel_sp = kstack + PGSIZE; // process's kernel stack + trapframe->kernel_trap = (uint64)usertrap; + trapframe->kernel_hartid = r_tp(); // hartid for cpuid() + + 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(p->pagetable); + userret((uint64)trapframe); +} \ No newline at end of file diff --git a/os/trap.h b/os/trap.h new file mode 100644 index 0000000..6096df4 --- /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(struct trapframe *trapframe, uint64 kstack); + +#endif // TRAP_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 From 3ec22ee3d33caefcb9a827bfefd2759fbe7f8440 Mon Sep 17 00:00:00 2001 From: rcy17 Date: Wed, 9 Mar 2022 20:39:08 +0800 Subject: [PATCH 3/7] ch3 --- Makefile | 12 +++-- os/defs.h | 1 + os/loader.c | 53 +++++++++++---------- os/loader.h | 13 +++--- os/log.h | 2 + os/main.c | 15 +++--- os/proc.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ os/proc.h | 50 ++++++++++++++++++++ os/sbi.c | 5 ++ os/sbi.h | 3 ++ os/switch.S | 40 ++++++++++++++++ os/syscall.c | 28 ++++++++++-- os/timer.c | 24 ++++++++++ os/timer.h | 19 ++++++++ os/trampoline.S | 6 +-- os/trap.c | 107 +++++++++++++++++++++++++++++-------------- os/trap.h | 2 +- 17 files changed, 415 insertions(+), 84 deletions(-) create mode 100644 os/proc.c create mode 100644 os/proc.h create mode 100644 os/switch.S create mode 100644 os/timer.c create mode 100644 os/timer.h diff --git a/Makefile b/Makefile index 37e4f16..acaec94 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,7 @@ .PHONY: clean build user all: build_kernel -LOG ?= error - K = os -U = user TOOLPREFIX = riscv64-unknown-elf- CC = $(TOOLPREFIX)gcc @@ -37,6 +34,9 @@ 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) @@ -89,7 +89,7 @@ build/kernel: $(OBJS) os/kernel_app.ld clean: rm -rf $(BUILDDIR) os/kernel_app.ld os/link_app.S - make -C $(U) clean + make -C user clean # BOARD BOARD ?= qemu @@ -118,8 +118,10 @@ debug: build/kernel .gdbinit CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | grep -oP 'ch\K[0-9]') +BASE ?= 0 + user: - make -C $(U) CHAPTER=$(CHAPTER) BASE=$(BASE) + make -C user CHAPTER=$(CHAPTER) BASE=$(BASE) test: user run diff --git a/os/defs.h b/os/defs.h index 2d66039..af65c52 100644 --- a/os/defs.h +++ b/os/defs.h @@ -4,6 +4,7 @@ #include "const.h" #include "log.h" #include "printf.h" +#include "proc.h" #include "riscv.h" #include "sbi.h" #include "string.h" diff --git a/os/loader.c b/os/loader.c index 6fc7c6b..71e264a 100644 --- a/os/loader.c +++ b/os/loader.c @@ -2,45 +2,52 @@ #include "defs.h" #include "trap.h" -static int app_cur, app_num; +static uint64 app_num; static uint64 *app_info_ptr; -extern char _app_num[], userret[], boot_stack_top[], ekernel[]; +extern char _app_num[], ekernel[]; +// 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() { if ((uint64)ekernel >= BASE_ADDRESS) { panic("kernel too large...\n"); } app_info_ptr = (uint64 *)_app_num; - app_cur = -1; app_num = *app_info_ptr; + app_info_ptr++; } -__attribute__((aligned(4096))) char user_stack[USER_STACK_SIZE]; -__attribute__((aligned(4096))) char trap_page[TRAP_PAGE_SIZE]; - -int load_app(uint64 *info) +// Load nth user app at +// [BASE_ADDRESS + n * MAX_APP_SIZE, BASE_ADDRESS + (n+1) * MAX_APP_SIZE) +int load_app(int n, uint64 *info) { - uint64 start = info[0], end = info[1], length = end - start; - memset((void *)BASE_ADDRESS, 0, MAX_APP_SIZE); - memmove((void *)BASE_ADDRESS, (void *)start, length); + uint64 start = info[n], end = info[n + 1], length = end - start; + memset((void *)BASE_ADDRESS + n * MAX_APP_SIZE, 0, MAX_APP_SIZE); + memmove((void *)BASE_ADDRESS + n * MAX_APP_SIZE, (void *)start, length); return length; } -int run_next_app() +// load all apps and init the corresponding `proc` structure. +int run_all_app() { - struct trapframe *trapframe = (struct trapframe *)trap_page; - app_cur++; - app_info_ptr++; - if (app_cur >= app_num) { - return -1; + for (int i = 0; i < app_num; ++i) { + struct proc *p = allocproc(); + struct trapframe *trapframe = p->trapframe; + load_app(i, app_info_ptr); + uint64 entry = BASE_ADDRESS + i * MAX_APP_SIZE; + tracef("load app %d at %p", i, entry); + trapframe->epc = entry; + trapframe->sp = (uint64)p->ustack + USER_STACK_SIZE; + p->state = RUNNABLE; } - infof("load and run app %d", app_cur); - uint64 length = load_app(app_info_ptr); - debugf("bin range = [%p, %p)", *app_info_ptr, *app_info_ptr + length); - memset(trapframe, 0, 4096); - trapframe->epc = BASE_ADDRESS; - trapframe->sp = (uint64)user_stack + USER_STACK_SIZE; - usertrapret(trapframe, (uint64)boot_stack_top); return 0; } \ No newline at end of file diff --git a/os/loader.h b/os/loader.h index 2448411..37e0b2c 100644 --- a/os/loader.h +++ b/os/loader.h @@ -1,15 +1,16 @@ -#ifndef BATCH_H -#define BATCH_H +#ifndef LOADER_H +#define LOADER_H #include "const.h" #include "types.h" +int finished(); void loader_init(); -int run_next_app(); +int run_all_app(); #define BASE_ADDRESS (0x80400000) #define MAX_APP_SIZE (0x20000) -#define USER_STACK_SIZE PAGE_SIZE -#define TRAP_PAGE_SIZE PAGE_SIZE +#define USER_STACK_SIZE (PAGE_SIZE) +#define TRAP_PAGE_SIZE (PAGE_SIZE) -#endif // BATCH_H \ No newline at end of file +#endif // LOADER_H \ No newline at end of file diff --git a/os/log.h b/os/log.h index f88a34f..06efe90 100644 --- a/os/log.h +++ b/os/log.h @@ -4,6 +4,7 @@ extern void printf(char *, ...); extern int threadid(); extern void dummy(int, ...); +extern void shutdown(); #if defined(LOG_LEVEL_ERROR) @@ -113,6 +114,7 @@ enum LOG_COLOR { 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 index de53bd0..1b3f7ec 100644 --- a/os/main.c +++ b/os/main.c @@ -1,13 +1,9 @@ #include "console.h" #include "defs.h" #include "loader.h" +#include "timer.h" #include "trap.h" -int threadid() -{ - return 0; -} - void clean_bss() { extern char s_bss[]; @@ -18,8 +14,11 @@ void clean_bss() void main() { clean_bss(); - printf("hello wrold!\n"); - trap_init(); + proc_init(); loader_init(); - run_next_app(); + trap_init(); + timer_init(); + run_all_app(); + infof("start scheduler!"); + scheduler(); } diff --git a/os/proc.c b/os/proc.c new file mode 100644 index 0000000..37c763b --- /dev/null +++ b/os/proc.c @@ -0,0 +1,119 @@ +#include "proc.h" +#include "defs.h" +#include "loader.h" +#include "trap.h" + +struct proc pool[NPROC]; +char kstack[NPROC][PAGE_SIZE]; +__attribute__((aligned(4096))) char ustack[NPROC][PAGE_SIZE]; +__attribute__((aligned(4096))) char trapframe[NPROC][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->ustack = (uint64)ustack[p - pool]; + p->trapframe = (struct trapframe *)trapframe[p - pool]; + } + 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; + memset(&p->context, 0, sizeof(p->context)); + memset(p->trapframe, 0, PAGE_SIZE); + memset((void *)p->kstack, 0, PAGE_SIZE); + p->context.ra = (uint64)usertrapret; + p->context.sp = p->kstack + PAGE_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) { + 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(); +} + +// Exit the current process. +void exit(int code) +{ + struct proc *p = curr_proc(); + infof("proc %d exit with %d", p->pid, code); + p->state = UNUSED; + finished(); + sched(); +} \ No newline at end of file diff --git a/os/proc.h b/os/proc.h new file mode 100644 index 0000000..c297c38 --- /dev/null +++ b/os/proc.h @@ -0,0 +1,50 @@ +#ifndef PROC_H +#define PROC_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 + uint64 ustack; // Virtual address of user stack + uint64 kstack; // Virtual address of kernel stack + struct trapframe *trapframe; // data page for trampoline.S + struct context context; // swtch() here to run process +}; + +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/sbi.c b/os/sbi.c index 31e9e43..5ded34d 100644 --- a/os/sbi.c +++ b/os/sbi.c @@ -36,4 +36,9 @@ int console_getchar() 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 index 8e98c2a..43afd01 100644 --- a/os/sbi.h +++ b/os/sbi.h @@ -1,8 +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/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 index 3d495b3..8d71fd2 100644 --- a/os/syscall.c +++ b/os/syscall.c @@ -2,6 +2,7 @@ #include "defs.h" #include "loader.h" #include "syscall_ids.h" +#include "timer.h" #include "trap.h" uint64 sys_write(int fd, char *str, uint len) @@ -17,18 +18,29 @@ uint64 sys_write(int fd, char *str, uint len) __attribute__((noreturn)) void sys_exit(int code) { - debugf("sysexit(%d)", code); - run_next_app(); - printf("ALL DONE\n"); - shutdown(); + exit(code); __builtin_unreachable(); } +uint64 sys_sched_yield() +{ + yield(); + return 0; +} + +uint64 sys_gettimeofday(TimeVal *val, int _tz) +{ + uint64 cycle = get_cycle(); + val->sec = cycle / CPU_FREQ; + val->usec = (cycle % CPU_FREQ) * 1000000 / CPU_FREQ; + return 0; +} + extern char trap_page[]; void syscall() { - struct trapframe *trapframe = (struct trapframe *)trap_page; + 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 }; @@ -41,6 +53,12 @@ void syscall() 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; default: ret = -1; errorf("unknown syscall %d", id); 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..d577a39 --- /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 { + 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 index 573e4c7..00b1b96 100644 --- a/os/trampoline.S +++ b/os/trampoline.S @@ -60,7 +60,7 @@ uservec: sd t5, 272(a0) sd t6, 280(a0) - # save the user a0 in p->trapframe->a0 + # save the user a0 in p->trapframe->a0 csrr t0, sscratch sd t0, 112(a0) @@ -84,8 +84,8 @@ userret: # a1: user page table, for satp. # switch to the user page table. - # csrw satp, a1 - # sfence.vma zero, zero + 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. diff --git a/os/trap.c b/os/trap.c index 08b365a..c051c37 100644 --- a/os/trap.c +++ b/os/trap.c @@ -2,64 +2,105 @@ #include "defs.h" #include "loader.h" #include "syscall.h" +#include "timer.h" -extern char trampoline[], uservec[], boot_stack_top[]; +extern char trampoline[], uservec[]; extern void *userret(uint64); +void kerneltrap() +{ + if ((r_sstatus() & SSTATUS_SPP) == 0) + panic("kerneltrap: not from supervisor mode"); + panic("trap from kernel\n"); +} + +// set up to take exceptions and traps while in the kernel. +void set_usertrap(void) +{ + w_stvec((uint64)uservec & ~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) { - w_stvec((uint64)uservec & ~0x3); + set_kerneltrap(); +} + +void unknown_trap() +{ + errorf("unknown trap: %p, stval = %p\n", r_scause(), r_stval()); + exit(-1); } // // handle an interrupt, exception, or system call from user space. // called from trampoline.S // -void usertrap(struct trapframe *trapframe) +void usertrap() { + set_kerneltrap(); + struct trapframe *trapframe = curr_proc()->trapframe; + if ((r_sstatus() & SSTATUS_SPP) != 0) panic("usertrap: not from user mode"); uint64 cause = r_scause(); - if (cause == UserEnvCall) { - trapframe->epc += 4; - syscall(); - return usertrapret(trapframe, (uint64)boot_stack_top); - } - switch (cause) { - case StoreMisaligned: - case StorePageFault: - case LoadMisaligned: - case LoadPageFault: - case InstructionMisaligned: - case InstructionPageFault: - errorf("%d in application, bad addr = %p, bad instruction = %p, core " - "dumped.", - cause, r_stval(), trapframe->epc); - break; - case IllegalInstruction: - errorf("IllegalInstruction in application, epc = %p, core dumped.", - trapframe->epc); - break; - default: - errorf("unknown trap: %p, stval = %p sepc = %p", r_scause(), - r_stval(), r_sepc()); - break; + if (cause & (1ULL << 63)) { + cause &= ~(1ULL << 63); + switch (cause) { + case SupervisorTimer: + tracef("time interrupt!\n"); + 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: + printf("%d in application, bad addr = %p, bad instruction = %p, " + "core dumped.\n", + cause, r_stval(), trapframe->epc); + exit(-2); + break; + case IllegalInstruction: + printf("IllegalInstruction in application, core dumped.\n"); + exit(-3); + break; + default: + unknown_trap(); + break; + } } - infof("switch to next app"); - run_next_app(); - printf("ALL DONE\n"); - shutdown(); + usertrapret(); } // // return to user space // -void usertrapret(struct trapframe *trapframe, uint64 kstack) +void usertrapret() { + set_usertrap(); + struct trapframe *trapframe = curr_proc()->trapframe; trapframe->kernel_satp = r_satp(); // kernel page table - trapframe->kernel_sp = kstack + PGSIZE; // process's kernel stack + trapframe->kernel_sp = + curr_proc()->kstack + PGSIZE; // process's kernel stack trapframe->kernel_trap = (uint64)usertrap; trapframe->kernel_hartid = r_tp(); // hartid for cpuid() diff --git a/os/trap.h b/os/trap.h index 6096df4..1ecfd28 100644 --- a/os/trap.h +++ b/os/trap.h @@ -69,6 +69,6 @@ enum Interrupt { }; void trap_init(); -void usertrapret(struct trapframe *trapframe, uint64 kstack); +void usertrapret(); #endif // TRAP_H \ No newline at end of file From b125038fbd686c6274e8065eb18e3f2c1ff23443 Mon Sep 17 00:00:00 2001 From: rcy17 Date: Wed, 9 Mar 2022 20:39:08 +0800 Subject: [PATCH 4/7] ch4 --- os/const.h | 24 ++++ os/defs.h | 2 + os/kalloc.c | 57 ++++++++++ os/kalloc.h | 8 ++ os/loader.c | 55 ++++++--- os/loader.h | 6 +- os/main.c | 5 +- os/proc.c | 24 ++-- os/proc.h | 5 +- os/riscv.h | 2 + os/syscall.c | 26 +++-- os/trampoline.S | 11 +- os/trap.c | 29 ++--- os/vm.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++ os/vm.h | 19 ++++ 15 files changed, 511 insertions(+), 59 deletions(-) create mode 100644 os/kalloc.c create mode 100644 os/kalloc.h create mode 100644 os/vm.c create mode 100644 os/vm.h diff --git a/os/const.h b/os/const.h index fa56854..74096d6 100644 --- a/os/const.h +++ b/os/const.h @@ -9,4 +9,28 @@ enum { 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 index af65c52..d0dbab2 100644 --- a/os/defs.h +++ b/os/defs.h @@ -2,6 +2,7 @@ #define DEFS_H #include "const.h" +#include "kalloc.h" #include "log.h" #include "printf.h" #include "proc.h" @@ -9,6 +10,7 @@ #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])) 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/loader.c b/os/loader.c index 71e264a..e1e1983 100644 --- a/os/loader.c +++ b/os/loader.c @@ -2,9 +2,9 @@ #include "defs.h" #include "trap.h" -static uint64 app_num; +static int app_num; static uint64 *app_info_ptr; -extern char _app_num[], ekernel[]; +extern char _app_num[]; // Count finished programs. If all apps exited, shutdown. int finished() @@ -18,22 +18,45 @@ int finished() // Get user progs' infomation through pre-defined symbol in `link_app.S` void loader_init() { - if ((uint64)ekernel >= BASE_ADDRESS) { - panic("kernel too large...\n"); - } app_info_ptr = (uint64 *)_app_num; app_num = *app_info_ptr; app_info_ptr++; } -// Load nth user app at -// [BASE_ADDRESS + n * MAX_APP_SIZE, BASE_ADDRESS + (n+1) * MAX_APP_SIZE) -int load_app(int n, uint64 *info) +pagetable_t bin_loader(uint64 start, uint64 end, struct proc *p) { - uint64 start = info[n], end = info[n + 1], length = end - start; - memset((void *)BASE_ADDRESS + n * MAX_APP_SIZE, 0, MAX_APP_SIZE); - memmove((void *)BASE_ADDRESS + n * MAX_APP_SIZE, (void *)start, length); - return length; + 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. @@ -41,12 +64,8 @@ int run_all_app() { for (int i = 0; i < app_num; ++i) { struct proc *p = allocproc(); - struct trapframe *trapframe = p->trapframe; - load_app(i, app_info_ptr); - uint64 entry = BASE_ADDRESS + i * MAX_APP_SIZE; - tracef("load app %d at %p", i, entry); - trapframe->epc = entry; - trapframe->sp = (uint64)p->ustack + USER_STACK_SIZE; + tracef("load app %d", i); + bin_loader(app_info_ptr[i], app_info_ptr[i + 1], p); p->state = RUNNABLE; } return 0; diff --git a/os/loader.h b/os/loader.h index 37e0b2c..bdd8e29 100644 --- a/os/loader.h +++ b/os/loader.h @@ -8,9 +8,9 @@ int finished(); void loader_init(); int run_all_app(); -#define BASE_ADDRESS (0x80400000) -#define MAX_APP_SIZE (0x20000) -#define USER_STACK_SIZE (PAGE_SIZE) +#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/main.c b/os/main.c index 1b3f7ec..714ab28 100644 --- a/os/main.c +++ b/os/main.c @@ -14,11 +14,14 @@ void clean_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/proc.c b/os/proc.c index 37c763b..467ee5e 100644 --- a/os/proc.c +++ b/os/proc.c @@ -2,11 +2,11 @@ #include "defs.h" #include "loader.h" #include "trap.h" +#include "vm.h" struct proc pool[NPROC]; -char kstack[NPROC][PAGE_SIZE]; -__attribute__((aligned(4096))) char ustack[NPROC][PAGE_SIZE]; -__attribute__((aligned(4096))) char trapframe[NPROC][PAGE_SIZE]; +__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; @@ -29,7 +29,6 @@ void proc_init(void) for (p = pool; p < &pool[NPROC]; p++) { p->state = UNUSED; p->kstack = (uint64)kstack[p - pool]; - p->ustack = (uint64)ustack[p - pool]; p->trapframe = (struct trapframe *)trapframe[p - pool]; } idle.kstack = (uint64)boot_stack_top; @@ -59,11 +58,14 @@ struct proc *allocproc(void) 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(p->trapframe, 0, PAGE_SIZE); - memset((void *)p->kstack, 0, PAGE_SIZE); + 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 + PAGE_SIZE; + p->context.sp = p->kstack + KSTACK_SIZE; return p; } @@ -108,12 +110,18 @@ void yield(void) 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); - p->state = UNUSED; + freeproc(p); finished(); sched(); } \ No newline at end of file diff --git a/os/proc.h b/os/proc.h index c297c38..1e933ad 100644 --- a/os/proc.h +++ b/os/proc.h @@ -1,6 +1,7 @@ #ifndef PROC_H #define PROC_H +#include "riscv.h" #include "types.h" #define NPROC (16) @@ -31,10 +32,12 @@ enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; struct proc { enum procstate state; // Process state int pid; // Process ID - uint64 ustack; // Virtual address of user stack + 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; }; struct proc *curr_proc(); diff --git a/os/riscv.h b/os/riscv.h index ba2fdbf..affaeef 100644 --- a/os/riscv.h +++ b/os/riscv.h @@ -291,6 +291,7 @@ static inline void sfence_vma() #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) @@ -317,6 +318,7 @@ static inline void sfence_vma() #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/syscall.c b/os/syscall.c index 8d71fd2..4ffc61c 100644 --- a/os/syscall.c +++ b/os/syscall.c @@ -5,15 +5,19 @@ #include "timer.h" #include "trap.h" -uint64 sys_write(int fd, char *str, uint len) +uint64 sys_write(int fd, uint64 va, uint len) { - debugf("sys_write fd = %d str = %x, len = %d", fd, str, len); + debugf("sys_write fd = %d va = %x, len = %d", fd, va, len); if (fd != STDOUT) return -1; - for (int i = 0; i < len; ++i) { + 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 len; + return size; } __attribute__((noreturn)) void sys_exit(int code) @@ -30,9 +34,15 @@ uint64 sys_sched_yield() uint64 sys_gettimeofday(TimeVal *val, int _tz) { - uint64 cycle = get_cycle(); - val->sec = cycle / CPU_FREQ; - val->usec = (cycle % CPU_FREQ) * 1000000 / CPU_FREQ; + // 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; } @@ -48,7 +58,7 @@ void syscall() args[1], args[2], args[3], args[4], args[5]); switch (id) { case SYS_write: - ret = sys_write(args[0], (char *)args[1], args[2]); + ret = sys_write(args[0], args[1], args[2]); break; case SYS_exit: sys_exit(args[0]); diff --git a/os/trampoline.S b/os/trampoline.S index 00b1b96..7af374b 100644 --- a/os/trampoline.S +++ b/os/trampoline.S @@ -8,7 +8,7 @@ # kernel.ld causes this to be aligned # to a page boundary. # - .section .text + .section trampsec .globl trampoline trampoline: .align 4 @@ -60,19 +60,16 @@ uservec: sd t5, 272(a0) sd t6, 280(a0) - # save the user a0 in p->trapframe->a0 csrr t0, sscratch sd t0, 112(a0) - csrr t1, sepc sd t1, 24(a0) - ld sp, 8(a0) ld tp, 32(a0) - ld t1, 0(a0) - # csrw satp, t1 - # sfence.vma zero, zero ld t0, 16(a0) + ld t1, 0(a0) + csrw satp, t1 + sfence.vma zero, zero jr t0 .globl userret diff --git a/os/trap.c b/os/trap.c index c051c37..173f944 100644 --- a/os/trap.c +++ b/os/trap.c @@ -5,19 +5,19 @@ #include "timer.h" extern char trampoline[], uservec[]; -extern void *userret(uint64); +extern char userret[]; void kerneltrap() { if ((r_sstatus() & SSTATUS_SPP) == 0) panic("kerneltrap: not from supervisor mode"); - panic("trap from kernel\n"); + panic("trap from kerne"); } // set up to take exceptions and traps while in the kernel. void set_usertrap(void) { - w_stvec((uint64)uservec & ~0x3); // DIRECT + w_stvec(((uint64)TRAMPOLINE + (uservec - trampoline)) & ~0x3); // DIRECT } void set_kerneltrap(void) @@ -28,12 +28,13 @@ void set_kerneltrap(void) // 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\n", r_scause(), r_stval()); + errorf("unknown trap: %p, stval = %p", r_scause(), r_stval()); exit(-1); } @@ -45,7 +46,7 @@ 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"); @@ -54,7 +55,7 @@ void usertrap() cause &= ~(1ULL << 63); switch (cause) { case SupervisorTimer: - tracef("time interrupt!\n"); + tracef("time interrupt!"); set_next_timer(); yield(); break; @@ -74,13 +75,13 @@ void usertrap() case InstructionPageFault: case LoadMisaligned: case LoadPageFault: - printf("%d in application, bad addr = %p, bad instruction = %p, " - "core dumped.\n", + errorf("%d in application, bad addr = %p, bad instruction = %p, " + "core dumped.", cause, r_stval(), trapframe->epc); exit(-2); break; case IllegalInstruction: - printf("IllegalInstruction in application, core dumped.\n"); + errorf("IllegalInstruction in application, core dumped."); exit(-3); break; default: @@ -100,9 +101,9 @@ void usertrapret() struct trapframe *trapframe = curr_proc()->trapframe; trapframe->kernel_satp = r_satp(); // kernel page table trapframe->kernel_sp = - curr_proc()->kstack + PGSIZE; // process's kernel stack + curr_proc()->kstack + KSTACK_SIZE; // process's kernel stack trapframe->kernel_trap = (uint64)usertrap; - trapframe->kernel_hartid = r_tp(); // hartid for cpuid() + trapframe->kernel_hartid = r_tp(); // unuesd w_sepc(trapframe->epc); // set up the registers that trampoline.S's sret will use @@ -115,6 +116,8 @@ void usertrapret() w_sstatus(x); // tell trampoline.S the user page table to switch to. - // uint64 satp = MAKE_SATP(p->pagetable); - userret((uint64)trapframe); + 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/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 From d141f72716744c9dacf5e35a5fddfec6b0e82c2c Mon Sep 17 00:00:00 2001 From: rcy17 Date: Wed, 9 Mar 2022 20:39:08 +0800 Subject: [PATCH 5/7] ch5 --- Makefile | 18 +++--- os/console.c | 5 ++ os/console.h | 1 + os/const.h | 2 + os/defs.h | 2 + os/kalloc.c | 2 +- os/kalloc.h | 4 +- os/kernelld.py | 2 +- os/loader.c | 118 +++++++++++++++++++++++--------------- os/loader.h | 5 +- os/main.c | 2 +- os/proc.c | 146 ++++++++++++++++++++++++++++++++++++++++++++---- os/proc.h | 18 +++++- os/queue.c | 29 ++++++++++ os/queue.h | 16 ++++++ os/syscall.c | 86 ++++++++++++++++++++++++---- os/trap.c | 6 +- os/vm.c | 50 ++++++++++++++--- os/vm.h | 5 +- scripts/pack.py | 13 +++++ 20 files changed, 437 insertions(+), 93 deletions(-) create mode 100644 os/queue.c create mode 100644 os/queue.h diff --git a/Makefile b/Makefile index acaec94..729931e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -.PHONY: clean build user -all: build_kernel +.PHONY: clean build user run debug test .FORCE +all: build K = os @@ -57,6 +57,9 @@ ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) CFLAGS += -fno-pie -nopie endif +# empty target +.FORCE: + LDFLAGS = -z max-page-size=4096 $(AS_OBJS): $(BUILDDIR)/$K/%.o : $K/%.S @@ -73,10 +76,12 @@ $(HEADER_DEP): $(BUILDDIR)/$K/%.d : $K/%.c sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ +INIT_PROC ?= usershell + 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 +os/link_app.S: scripts/pack.py .FORCE + @$(PY) scripts/pack.py $(INIT_PROC) +os/kernel_app.ld: scripts/kernelld.py .FORCE @$(PY) scripts/kernelld.py build: build/kernel @@ -89,7 +94,6 @@ build/kernel: $(OBJS) os/kernel_app.ld clean: rm -rf $(BUILDDIR) os/kernel_app.ld os/link_app.S - make -C user clean # BOARD BOARD ?= qemu @@ -118,8 +122,6 @@ debug: build/kernel .gdbinit CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | grep -oP 'ch\K[0-9]') -BASE ?= 0 - user: make -C user CHAPTER=$(CHAPTER) BASE=$(BASE) diff --git a/os/console.c b/os/console.c index f987a8b..ab0a1ca 100644 --- a/os/console.c +++ b/os/console.c @@ -9,4 +9,9 @@ void consputc(int c) void console_init() { // DO NOTHING +} + +int consgetc() +{ + return console_getchar(); } \ No newline at end of file diff --git a/os/console.h b/os/console.h index 7910ce5..f5b95b7 100644 --- a/os/console.h +++ b/os/console.h @@ -2,6 +2,7 @@ #define CONSOLE_H void consputc(int); +int consgetc(); void console_init(); #endif // CONSOLE_H diff --git a/os/const.h b/os/const.h index 74096d6..a178e6c 100644 --- a/os/const.h +++ b/os/const.h @@ -31,6 +31,8 @@ enum { // memory layout end +#define MAX_APP_NUM (32) #define MAX_STR_LEN (200) +#define IDLE_PID (0) #endif // CONST_H \ No newline at end of file diff --git a/os/defs.h b/os/defs.h index d0dbab2..6c7dbbc 100644 --- a/os/defs.h +++ b/os/defs.h @@ -17,4 +17,6 @@ #define MIN(a, b) (a < b ? a : b) #define MAX(a, b) (a > b ? a : b) +#define NULL ((void *)0) + #endif // DEF_H diff --git a/os/kalloc.c b/os/kalloc.c index 6f7012d..3c7c48b 100644 --- a/os/kalloc.c +++ b/os/kalloc.c @@ -45,7 +45,7 @@ void kfree(void *pa) // 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) +void *kalloc() { struct linklist *l; l = kmem.freelist; diff --git a/os/kalloc.h b/os/kalloc.h index c6936b6..86e2a36 100644 --- a/os/kalloc.h +++ b/os/kalloc.h @@ -1,8 +1,8 @@ #ifndef KALLOC_H #define KALLOC_H -void *kalloc(void); +void *kalloc(); void kfree(void *); -void kinit(void); +void kinit(); #endif // KALLOC_H \ No newline at end of file diff --git a/os/kernelld.py b/os/kernelld.py index 33beb91..6ba7ac0 100644 --- a/os/kernelld.py +++ b/os/kernelld.py @@ -38,7 +38,7 @@ *(.data) ''') for (idx, _) in enumerate(apps): - f.write(' . = ALIGN(0x1000);\n') + f.write(' . = ALIGN(0x8);\n') f.write(' *(.data.app{})\n'.format(idx)) f.write( ''' diff --git a/os/loader.c b/os/loader.c index e1e1983..361a941 100644 --- a/os/loader.c +++ b/os/loader.c @@ -4,69 +4,99 @@ 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; -} +extern char _app_num[], _app_names[], INIT_PROC[]; +char names[MAX_APP_NUM][MAX_STR_LEN]; // Get user progs' infomation through pre-defined symbol in `link_app.S` void loader_init() { + char *s; app_info_ptr = (uint64 *)_app_num; app_num = *app_info_ptr; app_info_ptr++; + s = _app_names; + printf("app list:\n"); + for (int i = 0; i < app_num; ++i) { + int len = strlen(s); + strncpy(names[i], (const char *)s, len); + s += len + 1; + printf("%s\n", names[i]); + } } -pagetable_t bin_loader(uint64 start, uint64 end, struct proc *p) +int get_id_by_name(char *name) { - 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); + for (int i = 0; i < app_num; ++i) { + if (strncmp(name, names[i], 100) == 0) + return i; } - 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"); + warnf("Cannot find such app %s", name); + return -1; +} + +int bin_loader(uint64 start, uint64 end, struct proc *p) +{ + if (p == NULL || p->state == UNUSED) + panic("..."); + void *page; + uint64 pa_start = PGROUNDDOWN(start); + uint64 pa_end = PGROUNDUP(end); + uint64 length = pa_end - pa_start; + uint64 va_start = BASE_ADDRESS; + uint64 va_end = BASE_ADDRESS + length; + for (uint64 va = va_start, pa = pa_start; pa < pa_end; + va += PGSIZE, pa += PGSIZE) { + page = kalloc(); + if (page == 0) { + panic("..."); + } + memmove(page, (const void *)pa, PGSIZE); + if (pa < start) { + memset(page, 0, start - va); + } else if (pa + PAGE_SIZE > end) { + memset(page + (end - pa), 0, PAGE_SIZE - (end - pa)); + } + if (mappages(p->pagetable, va, PGSIZE, (uint64)page, + PTE_U | PTE_R | PTE_W | PTE_X) != 0) + panic("..."); } - p->pagetable = pg; - uint64 ustack_bottom_vaddr = BASE_ADDRESS + length + PAGE_SIZE; - if (USTACK_SIZE != PAGE_SIZE) { - // Fix in ch5 - panic("Unsupported"); + // map ustack + p->ustack = va_end + PAGE_SIZE; + for (uint64 va = p->ustack; va < p->ustack + USTACK_SIZE; + va += PGSIZE) { + page = kalloc(); + if (page == 0) { + panic("..."); + } + memset(page, 0, PGSIZE); + if (mappages(p->pagetable, va, PGSIZE, (uint64)page, + PTE_U | PTE_R | PTE_W) != 0) + panic("..."); } - 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->trapframe->epc = va_start; p->max_page = PGROUNDUP(p->ustack + USTACK_SIZE - 1) / PAGE_SIZE; - return pg; + p->state = RUNNABLE; + return 0; +} + +int loader(int app_id, struct proc *p) +{ + return bin_loader(app_info_ptr[app_id], app_info_ptr[app_id + 1], p); } // load all apps and init the corresponding `proc` structure. -int run_all_app() +int load_init_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; + int id = get_id_by_name(INIT_PROC); + if (id < 0) + panic("Cannpt find INIT_PROC %s", INIT_PROC); + struct proc *p = allocproc(); + if (p == NULL) { + panic("allocproc\n"); } + debugf("load init proc %s", INIT_PROC); + loader(id, p); + add_task(p); return 0; } \ No newline at end of file diff --git a/os/loader.h b/os/loader.h index bdd8e29..36060d4 100644 --- a/os/loader.h +++ b/os/loader.h @@ -2,11 +2,14 @@ #define LOADER_H #include "const.h" +#include "proc.h" #include "types.h" int finished(); void loader_init(); -int run_all_app(); +int load_init_app(); +int loader(int, struct proc *); +int get_id_by_name(char *); #define BASE_ADDRESS (0x1000) #define USTACK_SIZE (PAGE_SIZE) diff --git a/os/main.c b/os/main.c index 714ab28..3380c44 100644 --- a/os/main.c +++ b/os/main.c @@ -21,7 +21,7 @@ void main() loader_init(); trap_init(); timer_init(); - run_all_app(); + load_init_app(); infof("start scheduler!"); scheduler(); } \ No newline at end of file diff --git a/os/proc.c b/os/proc.c index 467ee5e..d7caa6e 100644 --- a/os/proc.c +++ b/os/proc.c @@ -3,6 +3,7 @@ #include "loader.h" #include "trap.h" #include "vm.h" +#include "queue.h" struct proc pool[NPROC]; __attribute__((aligned(16))) char kstack[NPROC][PAGE_SIZE]; @@ -11,6 +12,7 @@ __attribute__((aligned(4096))) char trapframe[NPROC][TRAP_PAGE_SIZE]; extern char boot_stack_top[]; struct proc *current_proc; struct proc idle; +struct queue task_queue; int threadid() { @@ -23,7 +25,7 @@ struct proc *curr_proc() } // initialize the proc table at boot time. -void proc_init(void) +void proc_init() { struct proc *p; for (p = pool; p < &pool[NPROC]; p++) { @@ -32,8 +34,9 @@ void proc_init(void) p->trapframe = (struct trapframe *)trapframe[p - pool]; } idle.kstack = (uint64)boot_stack_top; - idle.pid = 0; + idle.pid = IDLE_PID; current_proc = &idle; + init_queue(&task_queue); } int allocpid() @@ -42,10 +45,27 @@ int allocpid() return PID++; } +struct proc *fetch_task() +{ + int index = pop_queue(&task_queue); + if (index < 0) { + debugf("No task to fetch\n"); + return NULL; + } + debugf("fetch task %d(pid=%d) to task queue\n", index, pool[index].pid); + return pool + index; +} + +void add_task(struct proc *p) +{ + push_queue(&task_queue, p - pool); + debugf("add task %d(pid=%d) to task queue\n", p - pool, p->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 *allocproc() { struct proc *p; for (p = pool; p < &pool[NPROC]; p++) { @@ -56,11 +76,14 @@ struct proc *allocproc(void) return 0; found: + // init proc p->pid = allocpid(); p->state = USED; - p->pagetable = 0; p->ustack = 0; p->max_page = 0; + p->parent = NULL; + p->exit_code = 0; + p->pagetable = uvmcreate((uint64)p->trapframe); memset(&p->context, 0, sizeof(p->context)); memset((void *)p->kstack, 0, KSTACK_SIZE); memset((void *)p->trapframe, 0, TRAP_PAGE_SIZE); @@ -74,17 +97,31 @@ struct proc *allocproc(void) // - swtch to start running that process. // - eventually that process transfers control // via swtch back to the scheduler. -void scheduler(void) +void scheduler() { struct proc *p; for (;;) { + /*int has_proc = 0; for (p = pool; p < &pool[NPROC]; p++) { if (p->state == RUNNABLE) { + has_proc = 1; + tracef("swtich to proc %d", p - pool); p->state = RUNNING; current_proc = p; swtch(&idle.context, &p->context); } } + if(has_proc == 0) { + panic("all app are over!\n"); + }*/ + p = fetch_task(); + if (p == NULL) { + panic("all app are over!\n"); + } + tracef("swtich to proc %d", p - pool); + p->state = RUNNING; + current_proc = p; + swtch(&idle.context, &p->context); } } @@ -95,7 +132,7 @@ void scheduler(void) // 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) +void sched() { struct proc *p = curr_proc(); if (p->state == RUNNING) @@ -104,24 +141,113 @@ void sched(void) } // Give up the CPU for one scheduling round. -void yield(void) +void yield() { current_proc->state = RUNNABLE; + add_task(current_proc); sched(); } +// Free a process's page table, and free the +// physical memory it refers to. +void freepagetable(pagetable_t pagetable, uint64 max_page) +{ + uvmunmap(pagetable, TRAMPOLINE, 1, 0); + uvmunmap(pagetable, TRAPFRAME, 1, 0); + uvmfree(pagetable, max_page); +} + void freeproc(struct proc *p) { + if (p->pagetable) + freepagetable(p->pagetable, p->max_page); + p->pagetable = 0; p->state = UNUSED; - // uvmfree(p->pagetable, p->max_page); +} + +int fork() +{ + struct proc *np; + struct proc *p = curr_proc(); + // Allocate process. + if ((np = allocproc()) == 0) { + panic("allocproc\n"); + } + // Copy user memory from parent to child. + if (uvmcopy(p->pagetable, np->pagetable, p->max_page) < 0) { + panic("uvmcopy\n"); + } + np->max_page = p->max_page; + // copy saved user registers. + *(np->trapframe) = *(p->trapframe); + // Cause fork to return 0 in the child. + np->trapframe->a0 = 0; + np->parent = p; + np->state = RUNNABLE; + add_task(np); + return np->pid; +} + +int exec(char *name) +{ + int id = get_id_by_name(name); + if (id < 0) + return -1; + struct proc *p = curr_proc(); + uvmunmap(p->pagetable, 0, p->max_page, 1); + p->max_page = 0; + loader(id, p); + return 0; +} + +int wait(int pid, int *code) +{ + struct proc *np; + int havekids; + struct proc *p = curr_proc(); + + for (;;) { + // Scan through table looking for exited children. + havekids = 0; + for (np = pool; np < &pool[NPROC]; np++) { + if (np->state != UNUSED && np->parent == p && + (pid <= 0 || np->pid == pid)) { + havekids = 1; + if (np->state == ZOMBIE) { + // Found one. + np->state = UNUSED; + pid = np->pid; + *code = np->exit_code; + return pid; + } + } + } + if (!havekids) { + return -1; + } + p->state = RUNNABLE; + add_task(p); + sched(); + } } // Exit the current process. void exit(int code) { struct proc *p = curr_proc(); - infof("proc %d exit with %d", p->pid, code); + p->exit_code = code; + debugf("proc %d exit with %d\n", p->pid, code); freeproc(p); - finished(); + if (p->parent != NULL) { + // Parent should `wait` + p->state = ZOMBIE; + } + // Set the `parent` of all children to NULL + struct proc *np; + for (np = pool; np < &pool[NPROC]; np++) { + if (np->parent == p) { + np->parent = NULL; + } + } sched(); } \ No newline at end of file diff --git a/os/proc.h b/os/proc.h index 1e933ad..2831608 100644 --- a/os/proc.h +++ b/os/proc.h @@ -3,8 +3,12 @@ #include "riscv.h" #include "types.h" +#include "queue.h" -#define NPROC (16) +#define NPROC (512) +#define FD_BUFFER_SIZE (16) + +struct file; // Saved registers for kernel context switches. struct context { @@ -33,20 +37,30 @@ struct proc { enum procstate state; // Process state int pid; // Process ID pagetable_t pagetable; // User page table - uint64 ustack; + uint64 ustack; // Virtual address of kernel stack 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; + struct proc *parent; // Parent process + uint64 exit_code; + struct file *files[FD_BUFFER_SIZE]; }; +int cpuid(); struct proc *curr_proc(); void exit(int); void proc_init(); void scheduler() __attribute__((noreturn)); void sched(); void yield(); +int fork(); +int exec(char *); +int wait(int, int *); +void add_task(struct proc *); +struct proc *pop_task(); struct proc *allocproc(); +int fdalloc(struct file *); // swtch.S void swtch(struct context *, struct context *); diff --git a/os/queue.c b/os/queue.c new file mode 100644 index 0000000..5f04063 --- /dev/null +++ b/os/queue.c @@ -0,0 +1,29 @@ +#include "queue.h" +#include "defs.h" + +void init_queue(struct queue *q) +{ + q->front = q->tail = 0; + q->empty = 1; +} + +void push_queue(struct queue *q, int value) +{ + if (!q->empty && q->front == q->tail) { + panic("queue shouldn't be overflow"); + } + q->empty = 0; + q->data[q->tail] = value; + q->tail = (q->tail + 1) % NPROC; +} + +int pop_queue(struct queue *q) +{ + if (q->empty) + return -1; + int value = q->data[q->front]; + q->front = (q->front + 1) % NPROC; + if (q->front == q->tail) + q->empty = 1; + return value; +} diff --git a/os/queue.h b/os/queue.h new file mode 100644 index 0000000..d692982 --- /dev/null +++ b/os/queue.h @@ -0,0 +1,16 @@ +#ifndef QUEUE_H +#define QUEUE_H +#define QUEUE_SIZE (1024) + +struct queue { + int data[QUEUE_SIZE]; + int front; + int tail; + int empty; +}; + +void init_queue(struct queue *); +void push_queue(struct queue *, int); +int pop_queue(struct queue *); + +#endif // QUEUE_H diff --git a/os/syscall.c b/os/syscall.c index 4ffc61c..7537f51 100644 --- a/os/syscall.c +++ b/os/syscall.c @@ -1,4 +1,5 @@ #include "syscall.h" +#include "console.h" #include "defs.h" #include "loader.h" #include "syscall_ids.h" @@ -7,7 +8,7 @@ uint64 sys_write(int fd, uint64 va, uint len) { - debugf("sys_write fd = %d va = %x, len = %d", fd, va, len); + debugf("sys_write fd = %d str = %x, len = %d", fd, va, len); if (fd != STDOUT) return -1; struct proc *p = curr_proc(); @@ -20,6 +21,21 @@ uint64 sys_write(int fd, uint64 va, uint len) return size; } +uint64 sys_read(int fd, uint64 va, uint64 len) +{ + debugf("sys_read fd = %d str = %x, len = %d", fd, va, len); + if (fd != STDIN) + return -1; + struct proc *p = curr_proc(); + char str[MAX_STR_LEN]; + for (int i = 0; i < len; ++i) { + int c = consgetc(); + str[i] = c; + } + copyout(p->pagetable, va, str, len); + return len; +} + __attribute__((noreturn)) void sys_exit(int code) { exit(code); @@ -32,18 +48,48 @@ uint64 sys_sched_yield() return 0; } -uint64 sys_gettimeofday(TimeVal *val, int _tz) +uint64 sys_gettimeofday(uint64 val, int _tz) { - // YOUR CODE - val->sec = 0; - val->usec = 0; + struct proc *p = curr_proc(); + uint64 cycle = get_cycle(); + TimeVal t; + t.sec = cycle / CPU_FREQ; + t.usec = (cycle % CPU_FREQ) * 1000000 / CPU_FREQ; + copyout(p->pagetable, val, (char *)&t, sizeof(TimeVal)); + return 0; +} - /* The code in `ch3` will leads to memory bugs*/ +uint64 sys_getpid() +{ + return curr_proc()->pid; +} - // uint64 cycle = get_cycle(); - // val->sec = cycle / CPU_FREQ; - // val->usec = (cycle % CPU_FREQ) * 1000000 / CPU_FREQ; - return 0; +uint64 sys_getppid() +{ + struct proc *p = curr_proc(); + return p->parent == NULL ? IDLE_PID : p->parent->pid; +} + +uint64 sys_clone() +{ + debugf("fork!\n"); + return fork(); +} + +uint64 sys_exec(uint64 va) +{ + struct proc *p = curr_proc(); + char name[200]; + copyinstr(p->pagetable, name, va, 200); + debugf("sys_exec %s\n", name); + return exec(name); +} + +uint64 sys_wait(int pid, uint64 va) +{ + struct proc *p = curr_proc(); + int *code = (int *)useraddr(p->pagetable, va); + return wait(pid, code); } extern char trap_page[]; @@ -60,6 +106,9 @@ void syscall() case SYS_write: ret = sys_write(args[0], args[1], args[2]); break; + case SYS_read: + ret = sys_read(args[0], args[1], args[2]); + break; case SYS_exit: sys_exit(args[0]); // __builtin_unreachable(); @@ -67,7 +116,22 @@ void syscall() ret = sys_sched_yield(); break; case SYS_gettimeofday: - ret = sys_gettimeofday((TimeVal *)args[0], args[1]); + ret = sys_gettimeofday(args[0], args[1]); + break; + case SYS_getpid: + ret = sys_getpid(); + break; + case SYS_getppid: + ret = sys_getppid(); + break; + case SYS_clone: // SYS_fork + ret = sys_clone(); + break; + case SYS_execve: + ret = sys_exec(args[0]); + break; + case SYS_wait4: + ret = sys_wait(args[0], args[1]); break; default: ret = -1; diff --git a/os/trap.c b/os/trap.c index 173f944..f241533 100644 --- a/os/trap.c +++ b/os/trap.c @@ -15,18 +15,18 @@ void kerneltrap() } // set up to take exceptions and traps while in the kernel. -void set_usertrap(void) +void set_usertrap() { w_stvec(((uint64)TRAMPOLINE + (uservec - trampoline)) & ~0x3); // DIRECT } -void set_kerneltrap(void) +void set_kerneltrap() { w_stvec((uint64)kerneltrap & ~0x3); // DIRECT } // set up to take exceptions and traps while in the kernel. -void trap_init(void) +void trap_init() { // intr_on(); set_kerneltrap(); diff --git a/os/vm.c b/os/vm.c index c5df5b6..c7eff11 100644 --- a/os/vm.c +++ b/os/vm.c @@ -8,7 +8,7 @@ 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 kvmmake() { pagetable_t kpgtbl; kpgtbl = (pagetable_t)kalloc(); @@ -26,7 +26,7 @@ pagetable_t kvmmake(void) // Initialize the one kernel_pagetable // Switch h/w page table register to the kernel's page table, // and enable paging. -void kvm_init(void) +void kvm_init() { kernel_pagetable = kvmmake(); w_satp(MAKE_SATP(kernel_pagetable)); @@ -117,8 +117,10 @@ int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) a = PGROUNDDOWN(va); last = PGROUNDDOWN(va + size - 1); for (;;) { - if ((pte = walk(pagetable, a, 1)) == 0) + if ((pte = walk(pagetable, a, 1)) == 0) { + errorf("pte invalid, va = %p", a); return -1; + } if (*pte & PTE_V) { errorf("remap"); return -1; @@ -160,7 +162,7 @@ void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) // create an empty user page table. // returns 0 if out of memory. -pagetable_t uvmcreate() +pagetable_t uvmcreate(uint64 trapframe) { pagetable_t pagetable; pagetable = (pagetable_t)kalloc(); @@ -171,9 +173,11 @@ pagetable_t uvmcreate() 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; + panic("mappages fail"); + } + if (mappages(pagetable, TRAPFRAME, PGSIZE, trapframe, PTE_R | PTE_W) < + 0) { + panic("mappages fail"); } return pagetable; } @@ -209,6 +213,38 @@ void uvmfree(pagetable_t pagetable, uint64 max_page) freewalk(pagetable); } +// Used in fork. +// Copy the pagetable page and all the user pages. +// Return 0 on success, -1 on error. +int uvmcopy(pagetable_t old, pagetable_t new, uint64 max_page) +{ + pte_t *pte; + uint64 pa, i; + uint flags; + char *mem; + + for (i = 0; i < max_page * PAGE_SIZE; i += PGSIZE) { + if ((pte = walk(old, i, 0)) == 0) + continue; + if ((*pte & PTE_V) == 0) + continue; + pa = PTE2PA(*pte); + flags = PTE_FLAGS(*pte); + if ((mem = kalloc()) == 0) + goto err; + memmove(mem, (char *)pa, PGSIZE); + if (mappages(new, i, PGSIZE, (uint64)mem, flags) != 0) { + kfree(mem); + goto err; + } + } + return 0; + +err: + uvmunmap(new, 0, i / PGSIZE, 1); + return -1; +} + // 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. diff --git a/os/vm.h b/os/vm.h index 05622ac..84bdc11 100644 --- a/os/vm.h +++ b/os/vm.h @@ -4,10 +4,11 @@ #include "riscv.h" #include "types.h" -void kvm_init(void); +void kvm_init(); void kvmmap(pagetable_t, uint64, uint64, uint64, int); int mappages(pagetable_t, uint64, uint64, uint64, int); -pagetable_t uvmcreate(void); +pagetable_t uvmcreate(uint64); +int uvmcopy(pagetable_t, pagetable_t, uint64); void uvmfree(pagetable_t, uint64); void uvmunmap(pagetable_t, uint64, uint64, int); uint64 walkaddr(pagetable_t, uint64); diff --git a/scripts/pack.py b/scripts/pack.py index c041b55..0f83870 100644 --- a/scripts/pack.py +++ b/scripts/pack.py @@ -2,7 +2,13 @@ TARGET_DIR = "./user/target/bin/" +import argparse + if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('INIT_PROC', default="usershell") + args = parser.parse_args() + f = open("os/link_app.S", mode="w") apps = os.listdir(TARGET_DIR) apps.sort() @@ -28,6 +34,13 @@ for app in apps: f.write(" .string \"" + app + "\"\n") + f.write( +''' + .global INIT_PROC +INIT_PROC: + .string \"{0}\" +'''.format(args.INIT_PROC)); + for (idx, app) in enumerate(apps): f.write( ''' From ced96aad2ac8d7a19e08fe84450c021c8e0f115c Mon Sep 17 00:00:00 2001 From: azd19 Date: Thu, 14 Apr 2022 14:12:19 +0800 Subject: [PATCH 6/7] update ch5 code --- os/queue.h | 2 ++ os/syscall.c | 15 +++++++++++++++ os/syscall_ids.h | 1 + 3 files changed, 18 insertions(+) diff --git a/os/queue.h b/os/queue.h index d692982..e039489 100644 --- a/os/queue.h +++ b/os/queue.h @@ -2,6 +2,8 @@ #define QUEUE_H #define QUEUE_SIZE (1024) +// TODO: change the queue to a priority queue sorted by priority + struct queue { int data[QUEUE_SIZE]; int front; diff --git a/os/syscall.c b/os/syscall.c index 7537f51..621fd9e 100644 --- a/os/syscall.c +++ b/os/syscall.c @@ -92,6 +92,18 @@ uint64 sys_wait(int pid, uint64 va) return wait(pid, code); } +uint64 sys_spawn(uint64 va) +{ + // TODO: your job is to complete the sys call + return -1; +} + +uint64 sys_set_priority(long long prio){ + // TODO: your job is to complete the sys call + return -1; +} + + extern char trap_page[]; void syscall() @@ -133,6 +145,9 @@ void syscall() case SYS_wait4: ret = sys_wait(args[0], args[1]); break; + case SYS_spawn: + ret = sys_spawn(args[0]); + break; default: ret = -1; errorf("unknown syscall %d", id); diff --git a/os/syscall_ids.h b/os/syscall_ids.h index fd765c8..fa18c6d 100644 --- a/os/syscall_ids.h +++ b/os/syscall_ids.h @@ -277,6 +277,7 @@ #define SYS_io_pgetevents 292 #define SYS_rseq 293 #define SYS_kexec_file_load 294 +#define SYS_spawn 400 #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426 From e4a493298833dc733b87d0acb34b3a1e08722413 Mon Sep 17 00:00:00 2001 From: KaitoD Date: Thu, 14 Apr 2022 16:50:15 +0800 Subject: [PATCH 7/7] update ch5 --- os/queue.h | 2 ++ os/syscall.c | 15 +++++++++++++++ os/syscall_ids.h | 1 + 3 files changed, 18 insertions(+) diff --git a/os/queue.h b/os/queue.h index d692982..e039489 100644 --- a/os/queue.h +++ b/os/queue.h @@ -2,6 +2,8 @@ #define QUEUE_H #define QUEUE_SIZE (1024) +// TODO: change the queue to a priority queue sorted by priority + struct queue { int data[QUEUE_SIZE]; int front; diff --git a/os/syscall.c b/os/syscall.c index 7537f51..621fd9e 100644 --- a/os/syscall.c +++ b/os/syscall.c @@ -92,6 +92,18 @@ uint64 sys_wait(int pid, uint64 va) return wait(pid, code); } +uint64 sys_spawn(uint64 va) +{ + // TODO: your job is to complete the sys call + return -1; +} + +uint64 sys_set_priority(long long prio){ + // TODO: your job is to complete the sys call + return -1; +} + + extern char trap_page[]; void syscall() @@ -133,6 +145,9 @@ void syscall() case SYS_wait4: ret = sys_wait(args[0], args[1]); break; + case SYS_spawn: + ret = sys_spawn(args[0]); + break; default: ret = -1; errorf("unknown syscall %d", id); diff --git a/os/syscall_ids.h b/os/syscall_ids.h index fd765c8..fa18c6d 100644 --- a/os/syscall_ids.h +++ b/os/syscall_ids.h @@ -277,6 +277,7 @@ #define SYS_io_pgetevents 292 #define SYS_rseq 293 #define SYS_kexec_file_load 294 +#define SYS_spawn 400 #define SYS_pidfd_send_signal 424 #define SYS_io_uring_setup 425 #define SYS_io_uring_enter 426