| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /*
- * Copyright (c) 2006-2025 RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2024-07-04 rcitach init ver.
- * 2025-04-22 ScuDays Add VDSO functionality under the riscv64 architecture.
- * 2026-04-21 rcitach Refactor vDSO runtime around the clock data page.
- */
- #include <rtthread.h>
- #include <mmu.h>
- #include <elf.h>
- #include <time.h>
- #include <lwp_user_mm.h>
- #include <drivers/clock_time.h>
- #ifdef RT_USING_RTC
- #include <rtdevice.h>
- #endif
- #include <vdso_kernel.h>
- #include <vdso_kernel_internal.h>
- #define DBG_TAG "vdso"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- enum rt_vdso_binary_id
- {
- RT_VDSO_BINARY_COMMON,
- };
- struct rt_vdso_binary_info
- {
- const char *name;
- const char *image_start;
- const char *image_end;
- unsigned long page_count;
- };
- static struct rt_vdso_binary_info rt_vdso_binaries[] = {
- [RT_VDSO_BINARY_COMMON] = {
- .name = "rt_vdso_common",
- .image_start = __rt_vdso_image_start,
- .image_end = __rt_vdso_image_end,
- },
- };
- static union
- {
- struct rt_vdso_data_page data_page;
- uint8_t raw[RT_VDSO_DATA_PAGE_COUNT * ARCH_PAGE_SIZE];
- } rt_vdso_data_page_store RT_VDSO_DATA_PAGE_ALIGNED;
- struct rt_vdso_data_page *rt_vdso_kernel_data_page = &rt_vdso_data_page_store.data_page;
- static struct rt_spinlock rt_vdso_data_page_lock;
- static int rt_vdso_runtime_status = RT_EOK;
- static struct timespec rt_vdso_realtime_offset;
- static rt_bool_t rt_vdso_realtime_offset_ready;
- #ifdef MMU_MAP_U_ROCB_XN
- #define RT_VDSO_DATA_PAGE_ATTR MMU_MAP_U_ROCB_XN
- #elif defined(MMU_MAP_U_ROCB)
- #define RT_VDSO_DATA_PAGE_ATTR MMU_MAP_U_ROCB
- #else
- #error "No user-read-only mapping available for vDSO data page!"
- #endif
- #define RT_VDSO_IMAGE_PAGE_ATTR MMU_MAP_U_ROCB
- static void rt_vdso_normalize_timespec(struct timespec *ts)
- {
- while (ts->tv_nsec >= RT_VDSO_NSEC_PER_SEC)
- {
- ts->tv_sec += 1;
- ts->tv_nsec -= RT_VDSO_NSEC_PER_SEC;
- }
- while (ts->tv_nsec < 0)
- {
- ts->tv_sec -= 1;
- ts->tv_nsec += RT_VDSO_NSEC_PER_SEC;
- }
- }
- static void rt_vdso_counter_to_timespec(rt_uint64_t counter_value,
- struct timespec *ts)
- {
- rt_uint64_t monotonic_ns;
- monotonic_ns = rt_clock_time_counter_to_ns(counter_value);
- ts->tv_sec = monotonic_ns / RT_VDSO_NSEC_PER_SEC;
- ts->tv_nsec = monotonic_ns % RT_VDSO_NSEC_PER_SEC;
- }
- static rt_uint64_t rt_vdso_read_arch_counter(void)
- {
- rt_uint64_t counter;
- #if defined(__aarch64__)
- __asm__ volatile("mrs %0, CNTVCT_EL0" : "=r"(counter));
- #elif defined(__arm__)
- rt_uint32_t lo;
- rt_uint32_t hi;
- __asm__ volatile("mrrc p15, 1, %0, %1, c14" : "=r"(lo), "=r"(hi));
- counter = ((rt_uint64_t)hi << 32) | lo;
- #elif defined(__riscv)
- __asm__ volatile("rdtime %0" : "=r"(counter));
- #else
- counter = rt_clock_time_get_counter();
- #endif
- return counter;
- }
- static struct timespec rt_vdso_timespec_subtract(const struct timespec *lhs,
- const struct timespec *rhs)
- {
- struct timespec ret;
- ret.tv_sec = lhs->tv_sec - rhs->tv_sec;
- ret.tv_nsec = lhs->tv_nsec - rhs->tv_nsec;
- rt_vdso_normalize_timespec(&ret);
- return ret;
- }
- static struct timespec rt_vdso_add_timespec(const struct timespec *lhs,
- const struct timespec *rhs)
- {
- struct timespec ret;
- ret.tv_sec = lhs->tv_sec + rhs->tv_sec;
- ret.tv_nsec = lhs->tv_nsec + rhs->tv_nsec;
- rt_vdso_normalize_timespec(&ret);
- return ret;
- }
- static int rt_vdso_read_monotonic_snapshot(struct timespec *monotonic_time,
- rt_uint64_t *counter_value,
- rt_uint64_t *counter_freq)
- {
- rt_uint64_t monotonic_counter;
- monotonic_counter = rt_clock_time_get_counter();
- *counter_freq = rt_clock_time_get_freq();
- if (*counter_freq == 0)
- {
- return -RT_ERROR;
- }
- /* User vDSO computes deltas from the raw architecture counter. */
- *counter_value = rt_vdso_read_arch_counter();
- rt_vdso_counter_to_timespec(monotonic_counter, monotonic_time);
- return RT_EOK;
- }
- static void rt_vdso_update_data_page_flags(void)
- {
- rt_uint64_t flags = 0;
- if (rt_vdso_realtime_offset_ready)
- {
- flags |= RT_VDSO_FLAG_REALTIME_VALID;
- }
- rt_vdso_kernel_data_page->flags = flags;
- }
- static void rt_vdso_store_clock_snapshot(const struct timespec *monotonic_time,
- rt_uint64_t counter_value,
- rt_uint64_t counter_freq)
- {
- rt_vdso_kernel_data_page->counter_last = counter_value;
- rt_vdso_kernel_data_page->counter_freq = counter_freq;
- rt_vdso_update_data_page_flags();
- rt_vdso_kernel_data_page->base_time[RT_VDSO_CLOCK_MONOTONIC_INDEX] = *monotonic_time;
- if (rt_vdso_realtime_offset_ready)
- {
- rt_vdso_kernel_data_page->base_time[RT_VDSO_CLOCK_REALTIME_INDEX] =
- rt_vdso_add_timespec(monotonic_time, &rt_vdso_realtime_offset);
- }
- else
- {
- rt_vdso_kernel_data_page->base_time[RT_VDSO_CLOCK_REALTIME_INDEX] = *monotonic_time;
- }
- }
- void rt_vdso_set_realtime(const struct timespec *realtime)
- {
- struct timespec monotonic;
- rt_uint64_t counter;
- rt_uint64_t freq;
- rt_base_t level;
- if (rt_vdso_runtime_status != RT_EOK || realtime == RT_NULL)
- {
- return;
- }
- if (rt_vdso_read_monotonic_snapshot(&monotonic, &counter, &freq) != RT_EOK)
- {
- return;
- }
- level = rt_spin_lock_irqsave(&rt_vdso_data_page_lock);
- rt_vdso_data_page_write_begin(rt_vdso_kernel_data_page);
- rt_vdso_realtime_offset = rt_vdso_timespec_subtract(realtime, &monotonic);
- rt_vdso_realtime_offset_ready = RT_TRUE;
- rt_vdso_store_clock_snapshot(&monotonic, counter, freq);
- rt_vdso_data_page_write_end(rt_vdso_kernel_data_page);
- rt_spin_unlock_irqrestore(&rt_vdso_data_page_lock, level);
- }
- static void *rt_vdso_map_physical_pages(struct rt_lwp *lwp, void *user_va,
- void *kernel_pa, size_t map_size,
- rt_size_t attr)
- {
- int err;
- char *va;
- size_t offset = 0;
- if (!map_size)
- {
- return RT_NULL;
- }
- if (user_va)
- {
- if (((size_t)user_va & ARCH_PAGE_MASK) !=
- ((size_t)kernel_pa & ARCH_PAGE_MASK))
- {
- return RT_NULL;
- }
- }
- offset = (size_t)kernel_pa & ARCH_PAGE_MASK;
- map_size += offset + ARCH_PAGE_SIZE - 1;
- map_size &= ~ARCH_PAGE_MASK;
- kernel_pa = (void *)((size_t)kernel_pa & ~ARCH_PAGE_MASK);
- struct rt_mm_va_hint hint = {
- .flags = 0,
- .limit_range_size = lwp->aspace->size,
- .limit_start = lwp->aspace->start,
- .prefer = user_va,
- .map_size = map_size,
- };
- if (user_va != RT_NULL)
- {
- hint.flags |= MMF_MAP_FIXED;
- }
- err = rt_aspace_map_phy(lwp->aspace, &hint, attr, MM_PA_TO_OFF(kernel_pa),
- (void **)&va);
- if (err != RT_EOK)
- {
- return RT_NULL;
- }
- return va + offset;
- }
- static int rt_vdso_map_binary_pages(enum rt_vdso_binary_id binary_id,
- struct rt_lwp *lwp)
- {
- void *data_page_base = RT_NULL;
- void *image_base = RT_NULL;
- unsigned long data_page_len;
- unsigned long image_len;
- RT_ASSERT(lwp != RT_NULL);
- data_page_len = RT_VDSO_DATA_PAGE_COUNT * ARCH_PAGE_SIZE;
- image_len = rt_vdso_binaries[binary_id].page_count << ARCH_PAGE_SHIFT;
- data_page_base = rt_vdso_map_physical_pages(
- lwp, RT_NULL, rt_kmem_v2p((void *)rt_vdso_get_kernel_data_page()),
- data_page_len, RT_VDSO_DATA_PAGE_ATTR);
- if (data_page_base == RT_NULL)
- {
- lwp->vdso_vbase = RT_NULL;
- return -RT_ERROR;
- }
- image_base = (uint8_t *)data_page_base + data_page_len;
- image_base = rt_vdso_map_physical_pages(
- lwp, image_base,
- rt_kmem_v2p((void *)rt_vdso_binaries[binary_id].image_start),
- image_len, RT_VDSO_IMAGE_PAGE_ATTR);
- if (image_base == RT_NULL)
- {
- lwp_unmap_user_phy(lwp, data_page_base);
- lwp->vdso_vbase = RT_NULL;
- return -RT_ERROR;
- }
- lwp->vdso_vbase = image_base;
- return RT_EOK;
- }
- int rt_vdso_map_process_image(struct rt_lwp *lwp)
- {
- if (rt_vdso_runtime_status != RT_EOK)
- {
- return -RT_ERROR;
- }
- return rt_vdso_map_binary_pages(RT_VDSO_BINARY_COMMON, lwp);
- }
- void rt_vdso_sync_clock_data(void)
- {
- struct timespec monotonic;
- rt_uint64_t counter;
- rt_uint64_t freq;
- if (rt_vdso_runtime_status != RT_EOK)
- {
- return;
- }
- if (rt_vdso_read_monotonic_snapshot(&monotonic, &counter, &freq) != RT_EOK)
- {
- return;
- }
- rt_spin_lock(&rt_vdso_data_page_lock);
- rt_vdso_data_page_write_begin(rt_vdso_kernel_data_page);
- rt_vdso_store_clock_snapshot(&monotonic, counter, freq);
- rt_vdso_data_page_write_end(rt_vdso_kernel_data_page);
- rt_spin_unlock(&rt_vdso_data_page_lock);
- }
- static int rt_vdso_validate_elf_header(const void *image)
- {
- const unsigned char *ident = (const unsigned char *)image;
- #if defined(__aarch64__)
- const Elf64_Ehdr *ehdr = (const Elf64_Ehdr *)image;
- if (ident[EI_CLASS] != ELFCLASS64)
- {
- LOG_E("vDSO ELF class mismatch: expect ELF64");
- return -RT_ERROR;
- }
- if (ehdr->e_type != ET_DYN)
- {
- LOG_E("vDSO ELF type mismatch!");
- return -RT_ERROR;
- }
- if (ehdr->e_machine != EM_AARCH64)
- {
- LOG_E("vDSO machine mismatch: expect AArch64");
- return -RT_ERROR;
- }
- #elif defined(__arm__)
- const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)image;
- if (ident[EI_CLASS] != ELFCLASS32)
- {
- LOG_E("vDSO ELF class mismatch: expect ELF32");
- return -RT_ERROR;
- }
- if (ehdr->e_type != ET_DYN)
- {
- LOG_E("vDSO ELF type mismatch!");
- return -RT_ERROR;
- }
- if (ehdr->e_machine != EM_ARM)
- {
- LOG_E("vDSO machine mismatch: expect ARM");
- return -RT_ERROR;
- }
- #elif defined(__riscv)
- const Elf64_Ehdr *ehdr = (const Elf64_Ehdr *)image;
- if (ident[EI_CLASS] != ELFCLASS64)
- {
- LOG_E("vDSO ELF class mismatch: expect ELF64");
- return -RT_ERROR;
- }
- if (ehdr->e_type != ET_DYN)
- {
- LOG_E("vDSO ELF type mismatch!");
- return -RT_ERROR;
- }
- if (ehdr->e_machine != EM_RISCV)
- {
- LOG_E("vDSO machine mismatch: expect RISC-V");
- return -RT_ERROR;
- }
- #else
- LOG_E("vDSO unsupported architecture!");
- return -RT_ERROR;
- #endif
- return RT_EOK;
- }
- static int rt_vdso_validate_image(void)
- {
- const void *image = rt_vdso_binaries[RT_VDSO_BINARY_COMMON].image_start;
- rt_memset(rt_vdso_data_page_store.raw, 0, sizeof(rt_vdso_data_page_store.raw));
- rt_vdso_realtime_offset_ready = RT_FALSE;
- rt_memset(&rt_vdso_realtime_offset, 0, sizeof(rt_vdso_realtime_offset));
- if (rt_memcmp(image, RT_VDSO_IMAGE_ELF_MAGIC, RT_VDSO_IMAGE_ELF_MAGIC_LEN))
- {
- LOG_E("vDSO is not a valid ELF object!");
- rt_vdso_runtime_status = -RT_ERROR;
- return -RT_ERROR;
- }
- if (rt_vdso_validate_elf_header(image) != RT_EOK)
- {
- rt_vdso_runtime_status = -RT_ERROR;
- return -RT_ERROR;
- }
- rt_vdso_binaries[RT_VDSO_BINARY_COMMON].page_count =
- (rt_vdso_binaries[RT_VDSO_BINARY_COMMON].image_end -
- rt_vdso_binaries[RT_VDSO_BINARY_COMMON].image_start) >>
- ARCH_PAGE_SHIFT;
- if (rt_vdso_binaries[RT_VDSO_BINARY_COMMON].page_count == 0)
- {
- LOG_E("vDSO image is empty!");
- rt_vdso_runtime_status = -RT_ERROR;
- return -RT_ERROR;
- }
- return RT_EOK;
- }
- INIT_COMPONENT_EXPORT(rt_vdso_validate_image);
|