| 4600 | | // #pragma mark - kernel public API |
|---|
| 4601 | | |
|---|
| 4602 | | |
|---|
| 4603 | | status_t |
|---|
| 4604 | | user_memcpy(void *to, const void *from, size_t size) |
|---|
| 4605 | | { |
|---|
| 4606 | | if (arch_cpu_user_memcpy(to, from, size, &thread_get_current_thread()->fault_handler) < B_OK) |
|---|
| 4607 | | return B_BAD_ADDRESS; |
|---|
| 4608 | | return B_OK; |
|---|
| 4609 | | } |
|---|
| 4610 | | |
|---|
| 4611 | | |
|---|
| 4612 | | /** \brief Copies at most (\a size - 1) characters from the string in \a from to |
|---|
| 4613 | | * the string in \a to, NULL-terminating the result. |
|---|
| 4614 | | * |
|---|
| 4615 | | * \param to Pointer to the destination C-string. |
|---|
| 4616 | | * \param from Pointer to the source C-string. |
|---|
| 4617 | | * \param size Size in bytes of the string buffer pointed to by \a to. |
|---|
| 4618 | | * |
|---|
| 4619 | | * \return strlen(\a from). |
|---|
| 4620 | | */ |
|---|
| 4621 | | |
|---|
| 4622 | | ssize_t |
|---|
| 4623 | | user_strlcpy(char *to, const char *from, size_t size) |
|---|
| 4624 | | { |
|---|
| 4625 | | return arch_cpu_user_strlcpy(to, from, size, &thread_get_current_thread()->fault_handler); |
|---|
| 4626 | | } |
|---|
| 4627 | | |
|---|
| 4628 | | |
|---|
| 4629 | | status_t |
|---|
| 4630 | | user_memset(void *s, char c, size_t count) |
|---|
| 4631 | | { |
|---|
| 4632 | | if (arch_cpu_user_memset(s, c, count, &thread_get_current_thread()->fault_handler) < B_OK) |
|---|
| 4633 | | return B_BAD_ADDRESS; |
|---|
| 4634 | | return B_OK; |
|---|
| 4635 | | } |
|---|
| 4636 | | |
|---|
| 4637 | | |
|---|
| 4638 | | long |
|---|
| 4639 | | lock_memory(void *address, ulong numBytes, ulong flags) |
|---|
| 4640 | | { |
|---|
| 4641 | | vm_address_space *addressSpace = NULL; |
|---|
| 4642 | | struct vm_translation_map *map; |
|---|
| 4643 | | addr_t unalignedBase = (addr_t)address; |
|---|
| 4644 | | addr_t end = unalignedBase + numBytes; |
|---|
| 4645 | | addr_t base = ROUNDOWN(unalignedBase, B_PAGE_SIZE); |
|---|
| 4646 | | bool isUser = IS_USER_ADDRESS(address); |
|---|
| 4647 | | bool needsLocking = true; |
|---|
| 4648 | | |
|---|
| 4649 | | if (isUser) |
|---|
| 4650 | | addressSpace = vm_get_current_user_address_space(); |
|---|
| 4651 | | else |
|---|
| 4652 | | addressSpace = vm_get_kernel_address_space(); |
|---|
| 4653 | | if (addressSpace == NULL) |
|---|
| 4654 | | return B_ERROR; |
|---|
| 4655 | | |
|---|
| 4656 | | // test if we're on an area that allows faults at all |
|---|
| 4657 | | |
|---|
| 4658 | | map = &addressSpace->translation_map; |
|---|
| 4659 | | |
|---|
| 4660 | | status_t status = test_lock_memory(addressSpace, base, needsLocking); |
|---|
| 4661 | | if (status < B_OK) |
|---|
| 4662 | | goto out; |
|---|
| 4663 | | if (!needsLocking) |
|---|
| 4664 | | goto out; |
|---|
| 4665 | | |
|---|
| 4666 | | for (; base < end; base += B_PAGE_SIZE) { |
|---|
| 4667 | | addr_t physicalAddress; |
|---|
| 4668 | | uint32 protection; |
|---|
| 4669 | | status_t status; |
|---|
| 4670 | | |
|---|
| 4671 | | map->ops->lock(map); |
|---|
| 4672 | | status = map->ops->query(map, base, &physicalAddress, &protection); |
|---|
| 4673 | | map->ops->unlock(map); |
|---|
| 4674 | | |
|---|
| 4675 | | if (status < B_OK) |
|---|
| 4676 | | goto out; |
|---|
| 4677 | | |
|---|
| 4678 | | if ((protection & PAGE_PRESENT) != 0) { |
|---|
| 4679 | | // if B_READ_DEVICE is set, the caller intents to write to the locked |
|---|
| 4680 | | // memory, so if it hasn't been mapped writable, we'll try the soft |
|---|
| 4681 | | // fault anyway |
|---|
| 4682 | | if ((flags & B_READ_DEVICE) == 0 |
|---|
| 4683 | | || (protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) { |
|---|
| 4684 | | // update wiring |
|---|
| 4685 | | vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); |
|---|
| 4686 | | if (page == NULL) |
|---|
| 4687 | | panic("couldn't lookup physical page just allocated\n"); |
|---|
| 4688 | | |
|---|
| 4689 | | page->wired_count++; |
|---|
| 4690 | | // TODO: needs to be atomic on all platforms! |
|---|
| 4691 | | continue; |
|---|
| 4692 | | } |
|---|
| 4693 | | } |
|---|
| 4694 | | |
|---|
| 4695 | | status = vm_soft_fault(base, (flags & B_READ_DEVICE) != 0, isUser); |
|---|
| 4696 | | if (status != B_OK) { |
|---|
| 4697 | | dprintf("lock_memory(address = %p, numBytes = %lu, flags = %lu) failed: %s\n", |
|---|
| 4698 | | (void *)unalignedBase, numBytes, flags, strerror(status)); |
|---|
| 4699 | | goto out; |
|---|
| 4700 | | } |
|---|
| 4701 | | |
|---|
| 4702 | | map->ops->lock(map); |
|---|
| 4703 | | status = map->ops->query(map, base, &physicalAddress, &protection); |
|---|
| 4704 | | map->ops->unlock(map); |
|---|
| 4705 | | |
|---|
| 4706 | | if (status < B_OK) |
|---|
| 4707 | | goto out; |
|---|
| 4708 | | |
|---|
| 4709 | | // update wiring |
|---|
| 4710 | | vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); |
|---|
| 4711 | | if (page == NULL) |
|---|
| 4712 | | panic("couldn't lookup physical page"); |
|---|
| 4713 | | |
|---|
| 4714 | | page->wired_count++; |
|---|
| 4715 | | // TODO: needs to be atomic on all platforms! |
|---|
| 4716 | | } |
|---|
| 4717 | | |
|---|
| 4718 | | out: |
|---|
| 4719 | | vm_put_address_space(addressSpace); |
|---|
| 4720 | | return status; |
|---|
| 4721 | | } |
|---|
| 4722 | | |
|---|
| 4723 | | |
|---|
| 4724 | | long |
|---|
| 4725 | | unlock_memory(void *address, ulong numBytes, ulong flags) |
|---|
| 4726 | | { |
|---|
| 4727 | | vm_address_space *addressSpace = NULL; |
|---|
| 4728 | | struct vm_translation_map *map; |
|---|
| 4729 | | addr_t unalignedBase = (addr_t)address; |
|---|
| 4730 | | addr_t end = unalignedBase + numBytes; |
|---|
| 4731 | | addr_t base = ROUNDOWN(unalignedBase, B_PAGE_SIZE); |
|---|
| 4732 | | bool needsLocking = true; |
|---|
| 4733 | | |
|---|
| 4734 | | if (IS_USER_ADDRESS(address)) |
|---|
| 4735 | | addressSpace = vm_get_current_user_address_space(); |
|---|
| 4736 | | else |
|---|
| 4737 | | addressSpace = vm_get_kernel_address_space(); |
|---|
| 4738 | | if (addressSpace == NULL) |
|---|
| 4739 | | return B_ERROR; |
|---|
| 4740 | | |
|---|
| 4741 | | map = &addressSpace->translation_map; |
|---|
| 4742 | | |
|---|
| 4743 | | status_t status = test_lock_memory(addressSpace, base, needsLocking); |
|---|
| 4744 | | if (status < B_OK) |
|---|
| 4745 | | goto out; |
|---|
| 4746 | | if (!needsLocking) |
|---|
| 4747 | | goto out; |
|---|
| 4748 | | |
|---|
| 4749 | | for (; base < end; base += B_PAGE_SIZE) { |
|---|
| 4750 | | map->ops->lock(map); |
|---|
| 4751 | | |
|---|
| 4752 | | addr_t physicalAddress; |
|---|
| 4753 | | uint32 protection; |
|---|
| 4754 | | status = map->ops->query(map, base, &physicalAddress, |
|---|
| 4755 | | &protection); |
|---|
| 4756 | | |
|---|
| 4757 | | map->ops->unlock(map); |
|---|
| 4758 | | |
|---|
| 4759 | | if (status < B_OK) |
|---|
| 4760 | | goto out; |
|---|
| 4761 | | if ((protection & PAGE_PRESENT) == 0) |
|---|
| 4762 | | panic("calling unlock_memory() on unmapped memory!"); |
|---|
| 4763 | | |
|---|
| 4764 | | // update wiring |
|---|
| 4765 | | vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); |
|---|
| 4766 | | if (page == NULL) |
|---|
| 4767 | | panic("couldn't lookup physical page"); |
|---|
| 4768 | | |
|---|
| 4769 | | page->wired_count--; |
|---|
| 4770 | | // TODO: needs to be atomic on all platforms! |
|---|
| 4771 | | } |
|---|
| 4772 | | |
|---|
| 4773 | | out: |
|---|
| 4774 | | vm_put_address_space(addressSpace); |
|---|
| 4775 | | return status; |
|---|
| 4776 | | } |
|---|
| 4777 | | |
|---|
| 4778 | | |
|---|
| 4779 | | /** According to the BeBook, this function should always succeed. |
|---|
| 4780 | | * This is no longer the case. |
|---|
| 4781 | | */ |
|---|
| 4782 | | |
|---|
| 4783 | | long |
|---|
| 4784 | | get_memory_map(const void *address, ulong numBytes, physical_entry *table, |
|---|
| 4785 | | long numEntries) |
|---|
| 4786 | | { |
|---|
| 4787 | | vm_address_space *addressSpace; |
|---|
| 4788 | | addr_t virtualAddress = (addr_t)address; |
|---|
| 4789 | | addr_t pageOffset = virtualAddress & (B_PAGE_SIZE - 1); |
|---|
| 4790 | | addr_t physicalAddress; |
|---|
| 4791 | | status_t status = B_OK; |
|---|
| 4792 | | int32 index = -1; |
|---|
| 4793 | | addr_t offset = 0; |
|---|
| 4794 | | bool interrupts = are_interrupts_enabled(); |
|---|
| 4795 | | |
|---|
| 4796 | | TRACE(("get_memory_map(%p, %lu bytes, %ld entries)\n", address, numBytes, |
|---|
| 4797 | | numEntries)); |
|---|
| 4798 | | |
|---|
| 4799 | | if (numEntries == 0 || numBytes == 0) |
|---|
| 4800 | | return B_BAD_VALUE; |
|---|
| 4801 | | |
|---|
| 4802 | | // in which address space is the address to be found? |
|---|
| 4803 | | if (IS_USER_ADDRESS(virtualAddress)) |
|---|
| 4804 | | addressSpace = thread_get_current_thread()->team->address_space; |
|---|
| 4805 | | else |
|---|
| 4806 | | addressSpace = vm_kernel_address_space(); |
|---|
| 4807 | | |
|---|
| 4808 | | if (addressSpace == NULL) |
|---|
| 4809 | | return B_ERROR; |
|---|
| 4810 | | |
|---|
| 4811 | | vm_translation_map *map = &addressSpace->translation_map; |
|---|
| 4812 | | |
|---|
| 4813 | | if (interrupts) |
|---|
| 4814 | | map->ops->lock(map); |
|---|
| 4815 | | |
|---|
| 4816 | | while (offset < numBytes) { |
|---|
| 4817 | | addr_t bytes = min_c(numBytes - offset, B_PAGE_SIZE); |
|---|
| 4818 | | uint32 flags; |
|---|
| 4819 | | |
|---|
| 4820 | | if (interrupts) { |
|---|
| 4821 | | status = map->ops->query(map, (addr_t)address + offset, |
|---|
| 4822 | | &physicalAddress, &flags); |
|---|
| 4823 | | } else { |
|---|
| 4824 | | status = map->ops->query_interrupt(map, (addr_t)address + offset, |
|---|
| 4825 | | &physicalAddress, &flags); |
|---|
| 4826 | | } |
|---|
| 4827 | | if (status < B_OK) |
|---|
| 4828 | | break; |
|---|
| 4829 | | if ((flags & PAGE_PRESENT) == 0) { |
|---|
| 4830 | | panic("get_memory_map() called on unmapped memory!"); |
|---|
| 4831 | | return B_BAD_ADDRESS; |
|---|
| 4832 | | } |
|---|
| 4833 | | |
|---|
| 4834 | | if (index < 0 && pageOffset > 0) { |
|---|
| 4835 | | physicalAddress += pageOffset; |
|---|
| 4836 | | if (bytes > B_PAGE_SIZE - pageOffset) |
|---|
| 4837 | | bytes = B_PAGE_SIZE - pageOffset; |
|---|
| 4838 | | } |
|---|
| 4839 | | |
|---|
| 4840 | | // need to switch to the next physical_entry? |
|---|
| 4841 | | if (index < 0 || (addr_t)table[index].address |
|---|
| 4842 | | != physicalAddress - table[index].size) { |
|---|
| 4843 | | if (++index + 1 > numEntries) { |
|---|
| 4844 | | // table to small |
|---|
| 4845 | | status = B_BUFFER_OVERFLOW; |
|---|
| 4846 | | break; |
|---|
| 4847 | | } |
|---|
| 4848 | | table[index].address = (void *)physicalAddress; |
|---|
| 4849 | | table[index].size = bytes; |
|---|
| 4850 | | } else { |
|---|
| 4851 | | // page does fit in current entry |
|---|
| 4852 | | table[index].size += bytes; |
|---|
| 4853 | | } |
|---|
| 4854 | | |
|---|
| 4855 | | offset += bytes; |
|---|
| 4856 | | } |
|---|
| 4857 | | |
|---|
| 4858 | | if (interrupts) |
|---|
| 4859 | | map->ops->unlock(map); |
|---|
| 4860 | | |
|---|
| 4861 | | // close the entry list |
|---|
| 4862 | | |
|---|
| 4863 | | if (status == B_OK) { |
|---|
| 4864 | | // if it's only one entry, we will silently accept the missing ending |
|---|
| 4865 | | if (numEntries == 1) |
|---|
| 4866 | | return B_OK; |
|---|
| 4867 | | |
|---|
| 4868 | | if (++index + 1 > numEntries) |
|---|
| 4869 | | return B_BUFFER_OVERFLOW; |
|---|
| 4870 | | |
|---|
| 4871 | | table[index].address = NULL; |
|---|
| 4872 | | table[index].size = 0; |
|---|
| 4873 | | } |
|---|
| 4874 | | |
|---|
| 4875 | | return status; |
|---|
| 4876 | | } |
|---|
| 4877 | | |
|---|
| 4878 | | |
|---|
| 4879 | | area_id |
|---|
| 4880 | | area_for(void *address) |
|---|
| 4881 | | { |
|---|
| 4882 | | team_id space; |
|---|
| 4883 | | |
|---|
| 4884 | | if (IS_USER_ADDRESS(address)) { |
|---|
| 4885 | | // we try the user team address space, if any |
|---|
| 4886 | | space = vm_current_user_address_space_id(); |
|---|
| 4887 | | if (space < B_OK) |
|---|
| 4888 | | return space; |
|---|
| 4889 | | } else |
|---|
| 4890 | | space = vm_kernel_address_space_id(); |
|---|
| 4891 | | |
|---|
| 4892 | | return vm_area_for(space, (addr_t)address); |
|---|
| 4893 | | } |
|---|
| 4894 | | |
|---|
| 4895 | | |
|---|
| 4896 | | area_id |
|---|
| 4897 | | find_area(const char *name) |
|---|
| 4898 | | { |
|---|
| 4899 | | acquire_sem_etc(sAreaHashLock, READ_COUNT, 0, 0); |
|---|
| 4900 | | struct hash_iterator iterator; |
|---|
| 4901 | | hash_open(sAreaHash, &iterator); |
|---|
| 4902 | | |
|---|
| 4903 | | vm_area *area; |
|---|
| 4904 | | area_id id = B_NAME_NOT_FOUND; |
|---|
| 4905 | | while ((area = (vm_area *)hash_next(sAreaHash, &iterator)) != NULL) { |
|---|
| 4906 | | if (area->id == RESERVED_AREA_ID) |
|---|
| 4907 | | continue; |
|---|
| 4908 | | |
|---|
| 4909 | | if (!strcmp(area->name, name)) { |
|---|
| 4910 | | id = area->id; |
|---|
| 4911 | | break; |
|---|
| 4912 | | } |
|---|
| 4913 | | } |
|---|
| 4914 | | |
|---|
| 4915 | | hash_close(sAreaHash, &iterator, false); |
|---|
| 4916 | | release_sem_etc(sAreaHashLock, READ_COUNT, 0); |
|---|
| 4917 | | |
|---|
| 4918 | | return id; |
|---|
| 4919 | | } |
|---|
| 4920 | | |
|---|
| 4921 | | |
|---|
| 4922 | | status_t |
|---|
| 4923 | | _get_area_info(area_id id, area_info *info, size_t size) |
|---|
| 4924 | | { |
|---|
| 4925 | | if (size != sizeof(area_info) || info == NULL) |
|---|
| 4926 | | return B_BAD_VALUE; |
|---|
| 4927 | | |
|---|
| 4928 | | AddressSpaceReadLocker locker; |
|---|
| 4929 | | vm_area *area; |
|---|
| 4930 | | status_t status = locker.SetFromArea(id, area); |
|---|
| 4931 | | if (status != B_OK) |
|---|
| 4932 | | return status; |
|---|
| 4933 | | |
|---|
| 4934 | | fill_area_info(area, info, size); |
|---|
| 4935 | | return B_OK; |
|---|
| 4936 | | } |
|---|
| 4937 | | |
|---|
| 4938 | | |
|---|
| 4939 | | status_t |
|---|
| 4940 | | _get_next_area_info(team_id team, int32 *cookie, area_info *info, size_t size) |
|---|
| 4941 | | { |
|---|
| 4942 | | addr_t nextBase = *(addr_t *)cookie; |
|---|
| 4943 | | |
|---|
| 4944 | | // we're already through the list |
|---|
| 4945 | | if (nextBase == (addr_t)-1) |
|---|
| 4946 | | return B_ENTRY_NOT_FOUND; |
|---|
| 4947 | | |
|---|
| 4948 | | if (team == B_CURRENT_TEAM) |
|---|
| 4949 | | team = team_get_current_team_id(); |
|---|
| 4950 | | |
|---|
| 4951 | | AddressSpaceReadLocker locker(team); |
|---|
| 4952 | | if (!locker.IsLocked()) |
|---|
| 4953 | | return B_BAD_TEAM_ID; |
|---|
| 4954 | | |
|---|
| 4955 | | vm_area *area; |
|---|
| 4956 | | for (area = locker.AddressSpace()->areas; area != NULL; |
|---|
| 4957 | | area = area->address_space_next) { |
|---|
| 4958 | | if (area->id == RESERVED_AREA_ID) |
|---|
| 4959 | | continue; |
|---|
| 4960 | | |
|---|
| 4961 | | if (area->base > nextBase) |
|---|
| 4962 | | break; |
|---|
| 4963 | | } |
|---|
| 4964 | | |
|---|
| 4965 | | if (area == NULL) { |
|---|
| 4966 | | nextBase = (addr_t)-1; |
|---|
| 4967 | | return B_ENTRY_NOT_FOUND; |
|---|
| 4968 | | } |
|---|
| 4969 | | |
|---|
| 4970 | | fill_area_info(area, info, size); |
|---|
| 4971 | | *cookie = (int32)(area->base); |
|---|
| 4972 | | |
|---|
| 4973 | | return B_OK; |
|---|
| 4974 | | } |
|---|
| 4975 | | |
|---|
| 4976 | | |
|---|
| 4977 | | status_t |
|---|
| 4978 | | set_area_protection(area_id area, uint32 newProtection) |
|---|
| 4979 | | { |
|---|
| 4980 | | fix_protection(&newProtection); |
|---|
| 4981 | | |
|---|
| 4982 | | return vm_set_area_protection(vm_kernel_address_space_id(), area, |
|---|
| 4983 | | newProtection); |
|---|
| 4984 | | } |
|---|
| 4985 | | |
|---|
| 4986 | | |
|---|
| 4987 | | status_t |
|---|
| 4988 | | resize_area(area_id areaID, size_t newSize) |
|---|
| | 4630 | static status_t |
|---|
| | 4631 | vm_resize_area(area_id areaID, size_t newSize, bool kernel) |
|---|
| | 4733 | } |
|---|
| | 4734 | |
|---|
| | 4735 | |
|---|
| | 4736 | // #pragma mark - kernel public API |
|---|
| | 4737 | |
|---|
| | 4738 | |
|---|
| | 4739 | status_t |
|---|
| | 4740 | user_memcpy(void *to, const void *from, size_t size) |
|---|
| | 4741 | { |
|---|
| | 4742 | if (arch_cpu_user_memcpy(to, from, size, &thread_get_current_thread()->fault_handler) < B_OK) |
|---|
| | 4743 | return B_BAD_ADDRESS; |
|---|
| | 4744 | return B_OK; |
|---|
| | 4745 | } |
|---|
| | 4746 | |
|---|
| | 4747 | |
|---|
| | 4748 | /** \brief Copies at most (\a size - 1) characters from the string in \a from to |
|---|
| | 4749 | * the string in \a to, NULL-terminating the result. |
|---|
| | 4750 | * |
|---|
| | 4751 | * \param to Pointer to the destination C-string. |
|---|
| | 4752 | * \param from Pointer to the source C-string. |
|---|
| | 4753 | * \param size Size in bytes of the string buffer pointed to by \a to. |
|---|
| | 4754 | * |
|---|
| | 4755 | * \return strlen(\a from). |
|---|
| | 4756 | */ |
|---|
| | 4757 | |
|---|
| | 4758 | ssize_t |
|---|
| | 4759 | user_strlcpy(char *to, const char *from, size_t size) |
|---|
| | 4760 | { |
|---|
| | 4761 | return arch_cpu_user_strlcpy(to, from, size, &thread_get_current_thread()->fault_handler); |
|---|
| | 4762 | } |
|---|
| | 4763 | |
|---|
| | 4764 | |
|---|
| | 4765 | status_t |
|---|
| | 4766 | user_memset(void *s, char c, size_t count) |
|---|
| | 4767 | { |
|---|
| | 4768 | if (arch_cpu_user_memset(s, c, count, &thread_get_current_thread()->fault_handler) < B_OK) |
|---|
| | 4769 | return B_BAD_ADDRESS; |
|---|
| | 4770 | return B_OK; |
|---|
| | 4771 | } |
|---|
| | 4772 | |
|---|
| | 4773 | |
|---|
| | 4774 | long |
|---|
| | 4775 | lock_memory(void *address, ulong numBytes, ulong flags) |
|---|
| | 4776 | { |
|---|
| | 4777 | vm_address_space *addressSpace = NULL; |
|---|
| | 4778 | struct vm_translation_map *map; |
|---|
| | 4779 | addr_t unalignedBase = (addr_t)address; |
|---|
| | 4780 | addr_t end = unalignedBase + numBytes; |
|---|
| | 4781 | addr_t base = ROUNDOWN(unalignedBase, B_PAGE_SIZE); |
|---|
| | 4782 | bool isUser = IS_USER_ADDRESS(address); |
|---|
| | 4783 | bool needsLocking = true; |
|---|
| | 4784 | |
|---|
| | 4785 | if (isUser) |
|---|
| | 4786 | addressSpace = vm_get_current_user_address_space(); |
|---|
| | 4787 | else |
|---|
| | 4788 | addressSpace = vm_get_kernel_address_space(); |
|---|
| | 4789 | if (addressSpace == NULL) |
|---|
| | 4790 | return B_ERROR; |
|---|
| | 4791 | |
|---|
| | 4792 | // test if we're on an area that allows faults at all |
|---|
| | 4793 | |
|---|
| | 4794 | map = &addressSpace->translation_map; |
|---|
| | 4795 | |
|---|
| | 4796 | status_t status = test_lock_memory(addressSpace, base, needsLocking); |
|---|
| | 4797 | if (status < B_OK) |
|---|
| | 4798 | goto out; |
|---|
| | 4799 | if (!needsLocking) |
|---|
| | 4800 | goto out; |
|---|
| | 4801 | |
|---|
| | 4802 | for (; base < end; base += B_PAGE_SIZE) { |
|---|
| | 4803 | addr_t physicalAddress; |
|---|
| | 4804 | uint32 protection; |
|---|
| | 4805 | status_t status; |
|---|
| | 4806 | |
|---|
| | 4807 | map->ops->lock(map); |
|---|
| | 4808 | status = map->ops->query(map, base, &physicalAddress, &protection); |
|---|
| | 4809 | map->ops->unlock(map); |
|---|
| | 4810 | |
|---|
| | 4811 | if (status < B_OK) |
|---|
| | 4812 | goto out; |
|---|
| | 4813 | |
|---|
| | 4814 | if ((protection & PAGE_PRESENT) != 0) { |
|---|
| | 4815 | // if B_READ_DEVICE is set, the caller intents to write to the locked |
|---|
| | 4816 | // memory, so if it hasn't been mapped writable, we'll try the soft |
|---|
| | 4817 | // fault anyway |
|---|
| | 4818 | if ((flags & B_READ_DEVICE) == 0 |
|---|
| | 4819 | || (protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) { |
|---|
| | 4820 | // update wiring |
|---|
| | 4821 | vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); |
|---|
| | 4822 | if (page == NULL) |
|---|
| | 4823 | panic("couldn't lookup physical page just allocated\n"); |
|---|
| | 4824 | |
|---|
| | 4825 | page->wired_count++; |
|---|
| | 4826 | // TODO: needs to be atomic on all platforms! |
|---|
| | 4827 | continue; |
|---|
| | 4828 | } |
|---|
| | 4829 | } |
|---|
| | 4830 | |
|---|
| | 4831 | status = vm_soft_fault(base, (flags & B_READ_DEVICE) != 0, isUser); |
|---|
| | 4832 | if (status != B_OK) { |
|---|
| | 4833 | dprintf("lock_memory(address = %p, numBytes = %lu, flags = %lu) failed: %s\n", |
|---|
| | 4834 | (void *)unalignedBase, numBytes, flags, strerror(status)); |
|---|
| | 4835 | goto out; |
|---|
| | 4836 | } |
|---|
| | 4837 | |
|---|
| | 4838 | map->ops->lock(map); |
|---|
| | 4839 | status = map->ops->query(map, base, &physicalAddress, &protection); |
|---|
| | 4840 | map->ops->unlock(map); |
|---|
| | 4841 | |
|---|
| | 4842 | if (status < B_OK) |
|---|
| | 4843 | goto out; |
|---|
| | 4844 | |
|---|
| | 4845 | // update wiring |
|---|
| | 4846 | vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); |
|---|
| | 4847 | if (page == NULL) |
|---|
| | 4848 | panic("couldn't lookup physical page"); |
|---|
| | 4849 | |
|---|
| | 4850 | page->wired_count++; |
|---|
| | 4851 | // TODO: needs to be atomic on all platforms! |
|---|
| | 4852 | } |
|---|
| | 4853 | |
|---|
| | 4854 | out: |
|---|
| | 4855 | vm_put_address_space(addressSpace); |
|---|
| | 4856 | return status; |
|---|
| | 4857 | } |
|---|
| | 4858 | |
|---|
| | 4859 | |
|---|
| | 4860 | long |
|---|
| | 4861 | unlock_memory(void *address, ulong numBytes, ulong flags) |
|---|
| | 4862 | { |
|---|
| | 4863 | vm_address_space *addressSpace = NULL; |
|---|
| | 4864 | struct vm_translation_map *map; |
|---|
| | 4865 | addr_t unalignedBase = (addr_t)address; |
|---|
| | 4866 | addr_t end = unalignedBase + numBytes; |
|---|
| | 4867 | addr_t base = ROUNDOWN(unalignedBase, B_PAGE_SIZE); |
|---|
| | 4868 | bool needsLocking = true; |
|---|
| | 4869 | |
|---|
| | 4870 | if (IS_USER_ADDRESS(address)) |
|---|
| | 4871 | addressSpace = vm_get_current_user_address_space(); |
|---|
| | 4872 | else |
|---|
| | 4873 | addressSpace = vm_get_kernel_address_space(); |
|---|
| | 4874 | if (addressSpace == NULL) |
|---|
| | 4875 | return B_ERROR; |
|---|
| | 4876 | |
|---|
| | 4877 | map = &addressSpace->translation_map; |
|---|
| | 4878 | |
|---|
| | 4879 | status_t status = test_lock_memory(addressSpace, base, needsLocking); |
|---|
| | 4880 | if (status < B_OK) |
|---|
| | 4881 | goto out; |
|---|
| | 4882 | if (!needsLocking) |
|---|
| | 4883 | goto out; |
|---|
| | 4884 | |
|---|
| | 4885 | for (; base < end; base += B_PAGE_SIZE) { |
|---|
| | 4886 | map->ops->lock(map); |
|---|
| | 4887 | |
|---|
| | 4888 | addr_t physicalAddress; |
|---|
| | 4889 | uint32 protection; |
|---|
| | 4890 | status = map->ops->query(map, base, &physicalAddress, |
|---|
| | 4891 | &protection); |
|---|
| | 4892 | |
|---|
| | 4893 | map->ops->unlock(map); |
|---|
| | 4894 | |
|---|
| | 4895 | if (status < B_OK) |
|---|
| | 4896 | goto out; |
|---|
| | 4897 | if ((protection & PAGE_PRESENT) == 0) |
|---|
| | 4898 | panic("calling unlock_memory() on unmapped memory!"); |
|---|
| | 4899 | |
|---|
| | 4900 | // update wiring |
|---|
| | 4901 | vm_page *page = vm_lookup_page(physicalAddress / B_PAGE_SIZE); |
|---|
| | 4902 | if (page == NULL) |
|---|
| | 4903 | panic("couldn't lookup physical page"); |
|---|
| | 4904 | |
|---|
| | 4905 | page->wired_count--; |
|---|
| | 4906 | // TODO: needs to be atomic on all platforms! |
|---|
| | 4907 | } |
|---|
| | 4908 | |
|---|
| | 4909 | out: |
|---|
| | 4910 | vm_put_address_space(addressSpace); |
|---|
| | 4911 | return status; |
|---|
| | 4912 | } |
|---|
| | 4913 | |
|---|
| | 4914 | |
|---|
| | 4915 | /** According to the BeBook, this function should always succeed. |
|---|
| | 4916 | * This is no longer the case. |
|---|
| | 4917 | */ |
|---|
| | 4918 | |
|---|
| | 4919 | long |
|---|
| | 4920 | get_memory_map(const void *address, ulong numBytes, physical_entry *table, |
|---|
| | 4921 | long numEntries) |
|---|
| | 4922 | { |
|---|
| | 4923 | vm_address_space *addressSpace; |
|---|
| | 4924 | addr_t virtualAddress = (addr_t)address; |
|---|
| | 4925 | addr_t pageOffset = virtualAddress & (B_PAGE_SIZE - 1); |
|---|
| | 4926 | addr_t physicalAddress; |
|---|
| | 4927 | status_t status = B_OK; |
|---|
| | 4928 | int32 index = -1; |
|---|
| | 4929 | addr_t offset = 0; |
|---|
| | 4930 | bool interrupts = are_interrupts_enabled(); |
|---|
| | 4931 | |
|---|
| | 4932 | TRACE(("get_memory_map(%p, %lu bytes, %ld entries)\n", address, numBytes, |
|---|
| | 4933 | numEntries)); |
|---|
| | 4934 | |
|---|
| | 4935 | if (numEntries == 0 || numBytes == 0) |
|---|
| | 4936 | return B_BAD_VALUE; |
|---|
| | 4937 | |
|---|
| | 4938 | // in which address space is the address to be found? |
|---|
| | 4939 | if (IS_USER_ADDRESS(virtualAddress)) |
|---|
| | 4940 | addressSpace = thread_get_current_thread()->team->address_space; |
|---|
| | 4941 | else |
|---|
| | 4942 | addressSpace = vm_kernel_address_space(); |
|---|
| | 4943 | |
|---|
| | 4944 | if (addressSpace == NULL) |
|---|
| | 4945 | return B_ERROR; |
|---|
| | 4946 | |
|---|
| | 4947 | vm_translation_map *map = &addressSpace->translation_map; |
|---|
| | 4948 | |
|---|
| | 4949 | if (interrupts) |
|---|
| | 4950 | map->ops->lock(map); |
|---|
| | 4951 | |
|---|
| | 4952 | while (offset < numBytes) { |
|---|
| | 4953 | addr_t bytes = min_c(numBytes - offset, B_PAGE_SIZE); |
|---|
| | 4954 | uint32 flags; |
|---|
| | 4955 | |
|---|
| | 4956 | if (interrupts) { |
|---|
| | 4957 | status = map->ops->query(map, (addr_t)address + offset, |
|---|
| | 4958 | &physicalAddress, &flags); |
|---|
| | 4959 | } else { |
|---|
| | 4960 | status = map->ops->query_interrupt(map, (addr_t)address + offset, |
|---|
| | 4961 | &physicalAddress, &flags); |
|---|
| | 4962 | } |
|---|
| | 4963 | if (status < B_OK) |
|---|
| | 4964 | break; |
|---|
| | 4965 | if ((flags & PAGE_PRESENT) == 0) { |
|---|
| | 4966 | panic("get_memory_map() called on unmapped memory!"); |
|---|
| | 4967 | return B_BAD_ADDRESS; |
|---|
| | 4968 | } |
|---|
| | 4969 | |
|---|
| | 4970 | if (index < 0 && pageOffset > 0) { |
|---|
| | 4971 | physicalAddress += pageOffset; |
|---|
| | 4972 | if (bytes > B_PAGE_SIZE - pageOffset) |
|---|
| | 4973 | bytes = B_PAGE_SIZE - pageOffset; |
|---|
| | 4974 | } |
|---|
| | 4975 | |
|---|
| | 4976 | // need to switch to the next physical_entry? |
|---|
| | 4977 | if (index < 0 || (addr_t)table[index].address |
|---|
| | 4978 | != physicalAddress - table[index].size) { |
|---|
| | 4979 | if (++index + 1 > numEntries) { |
|---|
| | 4980 | // table to small |
|---|
| | 4981 | status = B_BUFFER_OVERFLOW; |
|---|
| | 4982 | break; |
|---|
| | 4983 | } |
|---|
| | 4984 | table[index].address = (void *)physicalAddress; |
|---|
| | 4985 | table[index].size = bytes; |
|---|
| | 4986 | } else { |
|---|
| | 4987 | // page does fit in current entry |
|---|
| | 4988 | table[index].size += bytes; |
|---|
| | 4989 | } |
|---|
| | 4990 | |
|---|
| | 4991 | offset += bytes; |
|---|
| | 4992 | } |
|---|
| | 4993 | |
|---|
| | 4994 | if (interrupts) |
|---|
| | 4995 | map->ops->unlock(map); |
|---|
| | 4996 | |
|---|
| | 4997 | // close the entry list |
|---|
| | 4998 | |
|---|
| | 4999 | if (status == B_OK) { |
|---|
| | 5000 | // if it's only one entry, we will silently accept the missing ending |
|---|
| | 5001 | if (numEntries == 1) |
|---|
| | 5002 | return B_OK; |
|---|
| | 5003 | |
|---|
| | 5004 | if (++index + 1 > numEntries) |
|---|
| | 5005 | return B_BUFFER_OVERFLOW; |
|---|
| | 5006 | |
|---|
| | 5007 | table[index].address = NULL; |
|---|
| | 5008 | table[index].size = 0; |
|---|
| | 5009 | } |
|---|
| | 5010 | |
|---|
| | 5011 | return status; |
|---|
| | 5012 | } |
|---|
| | 5013 | |
|---|
| | 5014 | |
|---|
| | 5015 | area_id |
|---|
| | 5016 | area_for(void *address) |
|---|
| | 5017 | { |
|---|
| | 5018 | team_id space; |
|---|
| | 5019 | |
|---|
| | 5020 | if (IS_USER_ADDRESS(address)) { |
|---|
| | 5021 | // we try the user team address space, if any |
|---|
| | 5022 | space = vm_current_user_address_space_id(); |
|---|
| | 5023 | if (space < B_OK) |
|---|
| | 5024 | return space; |
|---|
| | 5025 | } else |
|---|
| | 5026 | space = vm_kernel_address_space_id(); |
|---|
| | 5027 | |
|---|
| | 5028 | return vm_area_for(space, (addr_t)address); |
|---|
| | 5029 | } |
|---|
| | 5030 | |
|---|
| | 5031 | |
|---|
| | 5032 | area_id |
|---|
| | 5033 | find_area(const char *name) |
|---|
| | 5034 | { |
|---|
| | 5035 | acquire_sem_etc(sAreaHashLock, READ_COUNT, 0, 0); |
|---|
| | 5036 | struct hash_iterator iterator; |
|---|
| | 5037 | hash_open(sAreaHash, &iterator); |
|---|
| | 5038 | |
|---|
| | 5039 | vm_area *area; |
|---|
| | 5040 | area_id id = B_NAME_NOT_FOUND; |
|---|
| | 5041 | while ((area = (vm_area *)hash_next(sAreaHash, &iterator)) != NULL) { |
|---|
| | 5042 | if (area->id == RESERVED_AREA_ID) |
|---|
| | 5043 | continue; |
|---|
| | 5044 | |
|---|
| | 5045 | if (!strcmp(area->name, name)) { |
|---|
| | 5046 | id = area->id; |
|---|
| | 5047 | break; |
|---|
| | 5048 | } |
|---|
| | 5049 | } |
|---|
| | 5050 | |
|---|
| | 5051 | hash_close(sAreaHash, &iterator, false); |
|---|
| | 5052 | release_sem_etc(sAreaHashLock, READ_COUNT, 0); |
|---|
| | 5053 | |
|---|
| | 5054 | return id; |
|---|
| | 5055 | } |
|---|
| | 5056 | |
|---|
| | 5057 | |
|---|
| | 5058 | status_t |
|---|
| | 5059 | _get_area_info(area_id id, area_info *info, size_t size) |
|---|
| | 5060 | { |
|---|
| | 5061 | if (size != sizeof(area_info) || info == NULL) |
|---|
| | 5062 | return B_BAD_VALUE; |
|---|
| | 5063 | |
|---|
| | 5064 | AddressSpaceReadLocker locker; |
|---|
| | 5065 | vm_area *area; |
|---|
| | 5066 | status_t status = locker.SetFromArea(id, area); |
|---|
| | 5067 | if (status != B_OK) |
|---|
| | 5068 | return status; |
|---|
| | 5069 | |
|---|
| | 5070 | fill_area_info(area, info, size); |
|---|
| | 5071 | return B_OK; |
|---|
| | 5072 | } |
|---|
| | 5073 | |
|---|
| | 5074 | |
|---|
| | 5075 | status_t |
|---|
| | 5076 | _get_next_area_info(team_id team, int32 *cookie, area_info *info, size_t size) |
|---|
| | 5077 | { |
|---|
| | 5078 | addr_t nextBase = *(addr_t *)cookie; |
|---|
| | 5079 | |
|---|
| | 5080 | // we're already through the list |
|---|
| | 5081 | if (nextBase == (addr_t)-1) |
|---|
| | 5082 | return B_ENTRY_NOT_FOUND; |
|---|
| | 5083 | |
|---|
| | 5084 | if (team == B_CURRENT_TEAM) |
|---|
| | 5085 | team = team_get_current_team_id(); |
|---|
| | 5086 | |
|---|
| | 5087 | AddressSpaceReadLocker locker(team); |
|---|
| | 5088 | if (!locker.IsLocked()) |
|---|
| | 5089 | return B_BAD_TEAM_ID; |
|---|
| | 5090 | |
|---|
| | 5091 | vm_area *area; |
|---|
| | 5092 | for (area = locker.AddressSpace()->areas; area != NULL; |
|---|
| | 5093 | area = area->address_space_next) { |
|---|
| | 5094 | if (area->id == RESERVED_AREA_ID) |
|---|
| | 5095 | continue; |
|---|
| | 5096 | |
|---|
| | 5097 | if (area->base > nextBase) |
|---|
| | 5098 |
|---|