Skip to content

Change various kernel APIs to work with pointers instead of vm_offset_t#2068

Open
bsdjhb wants to merge 19 commits intofreebsd:mainfrom
bsdjhb:pmap_pointer
Open

Change various kernel APIs to work with pointers instead of vm_offset_t#2068
bsdjhb wants to merge 19 commits intofreebsd:mainfrom
bsdjhb:pmap_pointer

Conversation

@bsdjhb
Copy link
Member

@bsdjhb bsdjhb commented Mar 9, 2026

In CHERI kernels, pointers are not the same size as addresses, and vm_offset_t represents a virtual address (ptraddr_t). To handle places that use vm_offset_t to hold pointers that can be dereferenced by the CPU, CheriBSD adds a new vm_pointer_t type (uintptr_t). However, using vm_pointer_t can be a bit fragile, as C compilers will silently accept conversions between uintptr_t and ptraddr_t. The resulting errors can only be found at runtime. However, if pointers are stored as a pointer type such as void *, then mismatches between addresses and pointers can be caught at compile time including when compiling on non-CHERI architectures. As such, this series converts various kernel pmap/virtual memory kernel APIs that currently use vm_offset_t to use pointer types (either void * or char *) instead. Normally void * is used, but char * is preferred if the API or structure member is commonly used in pointer arithmetic. In practice, these changes seem to be cleaner as most kernel consumers for many of these APIs had to cast between pointer types and vm_offset_t anyway and now those casts can be removed.

Note, when landing this series, I will add a commit to bump __FreeBSD_version so that kmods in ports can handle these changes, but it's pointless to add that commit until this is actually landed. This would also not be a MFC candidate.

Also, this seems too unwieldy to review in phabricator, hence reviewing here.

bsdjhb added 19 commits March 4, 2026 12:33
No functional change, but this is friendlier for CHERI.
This removes the need for several casts to pointer in callers.
Add explicit uintptr_t casts to the arguments to these macros so that
the work both with virtual addresses (e.g. vm_offset_t) and pointers.

Drop no-longer-needed casts in various invocations of DMAP_TO_PHYS.
Consistently use vm_paddr_t for the type returned from
moea64_bootstrap_alloc and avoid temporarily smuggling it via a
pointer.  Instead, be explicit in the places that assume a 1:1
mapping.
@bsdjhb bsdjhb requested a review from bsdimp as a code owner March 9, 2026 14:19
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Thank you for taking the time to contribute to FreeBSD!

Some of files have special handling:

Important

@bsdjhb @bryanv @markjdb @khng300 @markpeek wants to review changes to sys/amd64/vmm/

Important

@zxombie wants to review changes to sys/arm64/arm64

@mchoo7
Copy link
Contributor

mchoo7 commented Mar 11, 2026

The justification makes sense to me. What I worry is mixing void * and char * part. From the PR description and mailing list I get that char * is for pointer arithmetic, but it could be confusing for people who don't know the context (e.g. they might wonder rationale behind char *vaddr). This could end up like unsigned long devid for linux tasklet API where most people pass pointers instead of integers (I guess CHERI linux WG needs to handle this?)

My suggestion is creating a typedef to char *. In that case pointer arithmetic works and the type name makes more sense.


static int
pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot,
pmap_change_props_locked(void *addr, vm_size_t size, vm_prot_t prot,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this change. pmap_change_props() operates on address/page table, not on the pointer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this one was more about a tradeoff of casts in callers vs just one here, and yes it is a bit more squirrely.

int error;
bool changed;

va = (vm_offset_t)addr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the fact that you have to immediately cast the address to vm_offset_t aligns with my doubts.

("physical address %#jx not covered by the DMAP", \
(uintmax_t)x)); \
(x) + kva_layout.dmap_low; })
(void *)((x) + kva_layout.dmap_low); })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change is fine. But I suggest to provide a macro, lets call it PHYS_TO_DMAP_ADDR(), which is identical to the old PHYS_TO_DMAP(), which returns the address. Then PHYS_TO_DMAP() becomes ((void *)PHYS_TO_DMAP_ADDR()).

PHYS_TO_DMA_ADDR() should be used where you immediately cast PHYS_TO_DMAP() result back to address.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

#define VIRT_IN_DMAP(va) \
((va) >= kva_layout.dmap_low && (va) < kva_layout.dmap_low + dmaplimit)
#define VIRT_IN_DMAP(va) \
((uintptr_t)(va) >= kva_layout.dmap_low && \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to add a local of the uintptr_t type and evaluate and cast the 'va' arg only once.

if (pmap->pm_pmltopu != NULL) {
m = PHYS_TO_VM_PAGE(DMAP_TO_PHYS((vm_offset_t)pmap->
pm_pmltopu));
m = PHYS_TO_VM_PAGE(DMAP_TO_PHYS(pmap->pm_pmltopu));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be we need DMAP_TO_VM_PAGE() ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly this is a common pattern. Such a macro could also be MI as it would be spelled the same on all architectures.

@bsdjhb
Copy link
Member Author

bsdjhb commented Mar 11, 2026

The justification makes sense to me. What I worry is mixing void * and char * part. From the PR description and mailing list I get that char * is for pointer arithmetic, but it could be confusing for people who don't know the context (e.g. they might wonder rationale behind char *vaddr). This could end up like unsigned long devid for linux tasklet API where most people pass pointers instead of integers (I guess CHERI linux WG needs to handle this?)

My suggestion is creating a typedef to char *. In that case pointer arithmetic works and the type name makes more sense.

BSD has a legacy typedef (caddr_t), but we generally avoid that in new code. Also, I think the only place that might be char * in this series is td_kstack?

@mchoo7
Copy link
Contributor

mchoo7 commented Mar 11, 2026

The justification makes sense to me. What I worry is mixing void * and char * part. From the PR description and mailing list I get that char * is for pointer arithmetic, but it could be confusing for people who don't know the context (e.g. they might wonder rationale behind char *vaddr). This could end up like unsigned long devid for linux tasklet API where most people pass pointers instead of integers (I guess CHERI linux WG needs to handle this?)
My suggestion is creating a typedef to char *. In that case pointer arithmetic works and the type name makes more sense.

BSD has a legacy typedef (caddr_t), but we generally avoid that in new code.

I would avoid caddr_t for parameters, return values, and struct fields but for local variables caddr_t seems ok. I don't mind sticking with char * but the name caddr_t sounds more intuitive.

Also, I think the only place that might be char * in this series is td_kstack?

I see char * in many places like in struct sync_list.

But actually, we can address this by adding a note to style(9). e.g.:

Usage of caddr_t is avoided and void * is preferred. If there is frequent pointer arithmetic, char * is acceptable.

and use char * over caddr_t everywhere including local variables. Because new contributors are encouraged to start by reading style(9) they'll get the context before they work on the code.

Copy link
Member

@kostikbel kostikbel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one seems to be rather arbitrary. The patch adds more casts than it removes. Also, the kva is internal member which denotes the address which is used for page table manipulation (pmap_qenter()) and not a pointer that is operated upon. The single cast in sf_buf_kva() seems more proper to me.

@jrtc27
Copy link
Contributor

jrtc27 commented Mar 14, 2026

sf_buf_kva's returned value is used as a pointer, and in the non-DMAP + SFBUF case that pointer comes from the kva member. Yes, it's true that all supported CHERI architectures have a DMAP, but we should be consistent with types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants