Skip to content

Commit cab858f

Browse files
author
Delphix Engineering
committed
Merge branch 'refs/heads/upstream-HEAD' into repo-HEAD
2 parents dc65f95 + 32f5903 commit cab858f

29 files changed

+6041
-116
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ next
33
* Support flattened ELF dump files.
44
* Support partially rearranged makedumpfile split files.
55
* Parse QEMU CPU state ELF notes.
6+
* Use kernel page tables when initializing X86-64 Linux with PTI from
7+
CR3 register value.
8+
* Fix direct mapping if LDT PTI remapping is used in Linux on X86-64.
69
* Minor cache improvements and a NULL-pointer dereference fix.
710
* Fix test suite for 32-bit architectures.
811

src/addrxlat/x86_64.c

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@
133133
/** End of direct physical mapping with 5-level paging in 4.2+ */
134134
#define LINUX_DIRECTMAP_END_5L_4_2 0xff90ffffffffffff
135135

136+
/** Linux Page Table Isolation bit in CR3. */
137+
#define LINUX_PTI_USER_PGTABLE_MASK ((addrxlat_addr_t)1 << PAGE_SHIFT)
138+
136139
/** AMD64 (Intel 64) page table step function.
137140
* @param step Current step state.
138141
* @returns Error status.
@@ -249,7 +252,7 @@ static addrxlat_status
249252
linux_directmap_by_ver(struct sys_region *rgn, unsigned ver)
250253
{
251254
if (ver >= ADDRXLAT_VER_LINUX(4, 8, 0))
252-
return ADDRXLAT_ERR_NOMETH;
255+
return ADDRXLAT_ERR_NOTIMPL;
253256

254257
#define LINUX_DIRECTMAP_BY_VER(a, b, c) \
255258
if (ver >= ADDRXLAT_VER_LINUX(a, b, c)) { \
@@ -281,6 +284,41 @@ is_directmap(addrxlat_sys_t *sys, addrxlat_ctx_t *ctx,
281284
return status == ADDRXLAT_OK && addr == 0;
282285
}
283286

287+
/** Search for Linux directmap in the page tables.
288+
* @param rgn Directmap region; updated on success.
289+
* @param step Initial state for page table translation.
290+
* @returns Error status.
291+
*/
292+
static addrxlat_status
293+
linux_search_directmap(struct sys_region *rgn, addrxlat_step_t *step)
294+
{
295+
addrxlat_addr_t end;
296+
addrxlat_status status;
297+
298+
if (step->meth->param.pgt.pf.nfields == 6) {
299+
rgn->first = LINUX_DIRECTMAP_START_5L;
300+
end = LINUX_DIRECTMAP_END_5L_4_2;
301+
} else {
302+
rgn->first = LINUX_DIRECTMAP_START_2_6_31;
303+
end = LINUX_DIRECTMAP_END_4_2;
304+
}
305+
while (rgn->first < end) {
306+
status = lowest_mapped(step, &rgn->first, end);
307+
if (status != ADDRXLAT_OK)
308+
break;
309+
if (is_directmap(step->sys, step->ctx, rgn->first)) {
310+
rgn->last = rgn->first;
311+
return highest_linear(step, &rgn->last, end,
312+
-rgn->first);
313+
}
314+
status = lowest_unmapped(step, &rgn->first, end);
315+
if (status != ADDRXLAT_OK)
316+
break;
317+
}
318+
319+
return ADDRXLAT_ERR_NOTIMPL;
320+
}
321+
284322
/** Get directmap location by walking page tables.
285323
* @param rgn Directmap region; updated on success.
286324
* @param sys Translation system object.
@@ -295,8 +333,6 @@ linux_directmap_by_pgt(struct sys_region *rgn,
295333
addrxlat_sys_t *sys, addrxlat_ctx_t *ctx)
296334
{
297335
addrxlat_step_t step;
298-
addrxlat_addr_t end;
299-
addrxlat_status status;
300336

301337
step.ctx = ctx;
302338
step.sys = sys;
@@ -314,20 +350,7 @@ linux_directmap_by_pgt(struct sys_region *rgn,
314350
LINUX_DIRECTMAP_END_2_6_11, -rgn->first);
315351
}
316352

317-
if (step.meth->param.pgt.pf.nfields == 6) {
318-
rgn->first = LINUX_DIRECTMAP_START_5L;
319-
end = LINUX_DIRECTMAP_END_5L_4_2;
320-
} else {
321-
rgn->first = LINUX_DIRECTMAP_START_2_6_31;
322-
end = LINUX_DIRECTMAP_END_4_2;
323-
}
324-
status = lowest_mapped(&step, &rgn->first, end);
325-
if (status == ADDRXLAT_OK) {
326-
rgn->last = rgn->first;
327-
return highest_linear(&step, &rgn->last, end, -rgn->first);
328-
}
329-
330-
return ADDRXLAT_ERR_NOTIMPL;
353+
return linux_search_directmap(rgn, &step);
331354
}
332355

333356
/** Set up Linux direct mapping on x86_64.
@@ -340,6 +363,9 @@ linux_directmap(struct os_init_data *ctl)
340363
struct sys_region layout[2];
341364
addrxlat_status status;
342365

366+
if (ctl->sys->meth[ADDRXLAT_SYS_METH_DIRECT].kind != ADDRXLAT_NOMETH)
367+
return ADDRXLAT_OK;
368+
343369
status = linux_directmap_by_pgt(&layout[0], ctl->sys, ctl->ctx);
344370
if (status != ADDRXLAT_OK && opt_isset(ctl->popt, version_code))
345371
status = linux_directmap_by_ver(&layout[0],
@@ -350,10 +376,8 @@ linux_directmap(struct os_init_data *ctl)
350376
layout[0].act = SYS_ACT_DIRECT;
351377
layout[1].meth = ADDRXLAT_SYS_METH_NUM;
352378
status = sys_set_layout(ctl, ADDRXLAT_SYS_MAP_KV_PHYS, layout);
353-
if (status != ADDRXLAT_OK)
354-
return status;
355379
}
356-
return ADDRXLAT_OK;
380+
return status;
357381
}
358382

359383
/** Set the kernel text mapping offset.
@@ -691,46 +715,64 @@ set_xen_p2m(struct os_init_data *ctl)
691715
}
692716

693717
/** Get the top-level page table address for a Linux kernel.
694-
* @param ctx Address translation object.
695-
* @param addr Root page table address. Updated on success.
718+
* @param ctl Initialization data.
696719
* @returns Error status.
697720
*
698721
* It is not an error if the root page table address cannot be
699722
* determined; it merely stays uninitialized.
700723
*/
701724
static addrxlat_status
702-
get_linux_pgt_root(addrxlat_ctx_t *ctx, addrxlat_fulladdr_t *addr)
725+
get_linux_pgt_root(struct os_init_data *ctl)
703726
{
704727
static const char err_fmt[] = "Cannot resolve \"%s\"";
728+
addrxlat_fulladdr_t *addr;
705729
addrxlat_status status;
706730

731+
addr = &ctl->sys->meth[ADDRXLAT_SYS_METH_PGT].param.pgt.root;
707732
if (addr->as != ADDRXLAT_NOADDR)
708733
return ADDRXLAT_OK;
709734

710-
status = get_symval(ctx, "init_top_pgt", &addr->addr);
735+
status = get_reg(ctl->ctx, "cr3", &addr->addr);
736+
if (status == ADDRXLAT_OK) {
737+
addr->addr &= ~PAGE_MASK;
738+
addr->as = ADDRXLAT_MACHPHYSADDR;
739+
if (!(addr->addr & LINUX_PTI_USER_PGTABLE_MASK))
740+
return status;
741+
status = linux_directmap(ctl);
742+
if (status == ADDRXLAT_ERR_NOTIMPL) {
743+
addr->addr &= ~LINUX_PTI_USER_PGTABLE_MASK;
744+
status = linux_directmap(ctl);
745+
if (status == ADDRXLAT_OK)
746+
return status;
747+
addr->addr |= LINUX_PTI_USER_PGTABLE_MASK;
748+
}
749+
} else if (status != ADDRXLAT_ERR_NODATA)
750+
return set_error(ctl->ctx, status, err_fmt, "cr3");
751+
clear_error(ctl->ctx);
752+
753+
status = get_symval(ctl->ctx, "swapper_pg_dir", &addr->addr);
711754
if (status == ADDRXLAT_OK) {
712755
addr->as = ADDRXLAT_KVADDR;
713756
return status;
714757
} else if (status != ADDRXLAT_ERR_NODATA)
715-
return set_error(ctx, status, err_fmt, "init_top_pgt");
716-
clear_error(ctx);
758+
return set_error(ctl->ctx, status, err_fmt, "swapper_pg_dir");
759+
clear_error(ctl->ctx);
717760

718-
status = get_symval(ctx, "init_level4_pgt", &addr->addr);
761+
status = get_symval(ctl->ctx, "init_top_pgt", &addr->addr);
719762
if (status == ADDRXLAT_OK) {
720763
addr->as = ADDRXLAT_KVADDR;
721764
return status;
722765
} else if (status != ADDRXLAT_ERR_NODATA)
723-
return set_error(ctx, status, err_fmt, "init_level4_pgt");
724-
clear_error(ctx);
766+
return set_error(ctl->ctx, status, err_fmt, "init_top_pgt");
767+
clear_error(ctl->ctx);
725768

726-
status = get_reg(ctx, "cr3", &addr->addr);
769+
status = get_symval(ctl->ctx, "init_level4_pgt", &addr->addr);
727770
if (status == ADDRXLAT_OK) {
728-
addr->addr &= ~PAGE_MASK;
729-
addr->as = ADDRXLAT_MACHPHYSADDR;
771+
addr->as = ADDRXLAT_KVADDR;
730772
return status;
731773
} else if (status != ADDRXLAT_ERR_NODATA)
732-
return set_error(ctx, status, err_fmt, "cr3");
733-
clear_error(ctx);
774+
return set_error(ctl->ctx, status, err_fmt, "init_level4_pgt");
775+
clear_error(ctl->ctx);
734776

735777
return ADDRXLAT_OK;
736778
}
@@ -742,21 +784,20 @@ get_linux_pgt_root(addrxlat_ctx_t *ctx, addrxlat_fulladdr_t *addr)
742784
static addrxlat_status
743785
map_linux_x86_64(struct os_init_data *ctl)
744786
{
745-
addrxlat_meth_t *meth;
746787
addrxlat_addr_t sme_mask;
747788
unsigned long read_caps;
748789
addrxlat_status status;
749790

750791
/* Set up page table translation. */
751-
meth = &ctl->sys->meth[ADDRXLAT_SYS_METH_PGT];
752-
status = get_linux_pgt_root(ctl->ctx, &meth->param.pgt.root);
792+
status = get_linux_pgt_root(ctl);
753793
if (status != ADDRXLAT_OK)
754794
return set_error(ctl->ctx, status,
755795
"Cannot determine root page table");
756796

757797
status = get_number(ctl->ctx, "sme_mask", &sme_mask);
758798
if (status == ADDRXLAT_OK)
759-
meth->param.pgt.pte_mask = sme_mask;
799+
ctl->sys->meth[ADDRXLAT_SYS_METH_PGT].param.pgt.pte_mask =
800+
sme_mask;
760801
else if (status == ADDRXLAT_ERR_NODATA)
761802
clear_error(ctl->ctx);
762803
else
@@ -796,7 +837,7 @@ map_linux_x86_64(struct os_init_data *ctl)
796837

797838
/* Set up direct mapping. */
798839
status = linux_directmap(ctl);
799-
if (status != ADDRXLAT_OK)
840+
if (status != ADDRXLAT_OK && status != ADDRXLAT_ERR_NOTIMPL)
800841
return status;
801842

802843
return ADDRXLAT_OK;

0 commit comments

Comments
 (0)