133
133
/** End of direct physical mapping with 5-level paging in 4.2+ */
134
134
#define LINUX_DIRECTMAP_END_5L_4_2 0xff90ffffffffffff
135
135
136
+ /** Linux Page Table Isolation bit in CR3. */
137
+ #define LINUX_PTI_USER_PGTABLE_MASK ((addrxlat_addr_t)1 << PAGE_SHIFT)
138
+
136
139
/** AMD64 (Intel 64) page table step function.
137
140
* @param step Current step state.
138
141
* @returns Error status.
@@ -249,7 +252,7 @@ static addrxlat_status
249
252
linux_directmap_by_ver (struct sys_region * rgn , unsigned ver )
250
253
{
251
254
if (ver >= ADDRXLAT_VER_LINUX (4 , 8 , 0 ))
252
- return ADDRXLAT_ERR_NOMETH ;
255
+ return ADDRXLAT_ERR_NOTIMPL ;
253
256
254
257
#define LINUX_DIRECTMAP_BY_VER (a , b , c ) \
255
258
if (ver >= ADDRXLAT_VER_LINUX(a, b, c)) { \
@@ -281,6 +284,41 @@ is_directmap(addrxlat_sys_t *sys, addrxlat_ctx_t *ctx,
281
284
return status == ADDRXLAT_OK && addr == 0 ;
282
285
}
283
286
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
+
284
322
/** Get directmap location by walking page tables.
285
323
* @param rgn Directmap region; updated on success.
286
324
* @param sys Translation system object.
@@ -295,8 +333,6 @@ linux_directmap_by_pgt(struct sys_region *rgn,
295
333
addrxlat_sys_t * sys , addrxlat_ctx_t * ctx )
296
334
{
297
335
addrxlat_step_t step ;
298
- addrxlat_addr_t end ;
299
- addrxlat_status status ;
300
336
301
337
step .ctx = ctx ;
302
338
step .sys = sys ;
@@ -314,20 +350,7 @@ linux_directmap_by_pgt(struct sys_region *rgn,
314
350
LINUX_DIRECTMAP_END_2_6_11 , - rgn -> first );
315
351
}
316
352
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 );
331
354
}
332
355
333
356
/** Set up Linux direct mapping on x86_64.
@@ -340,6 +363,9 @@ linux_directmap(struct os_init_data *ctl)
340
363
struct sys_region layout [2 ];
341
364
addrxlat_status status ;
342
365
366
+ if (ctl -> sys -> meth [ADDRXLAT_SYS_METH_DIRECT ].kind != ADDRXLAT_NOMETH )
367
+ return ADDRXLAT_OK ;
368
+
343
369
status = linux_directmap_by_pgt (& layout [0 ], ctl -> sys , ctl -> ctx );
344
370
if (status != ADDRXLAT_OK && opt_isset (ctl -> popt , version_code ))
345
371
status = linux_directmap_by_ver (& layout [0 ],
@@ -350,10 +376,8 @@ linux_directmap(struct os_init_data *ctl)
350
376
layout [0 ].act = SYS_ACT_DIRECT ;
351
377
layout [1 ].meth = ADDRXLAT_SYS_METH_NUM ;
352
378
status = sys_set_layout (ctl , ADDRXLAT_SYS_MAP_KV_PHYS , layout );
353
- if (status != ADDRXLAT_OK )
354
- return status ;
355
379
}
356
- return ADDRXLAT_OK ;
380
+ return status ;
357
381
}
358
382
359
383
/** Set the kernel text mapping offset.
@@ -691,46 +715,64 @@ set_xen_p2m(struct os_init_data *ctl)
691
715
}
692
716
693
717
/** 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.
696
719
* @returns Error status.
697
720
*
698
721
* It is not an error if the root page table address cannot be
699
722
* determined; it merely stays uninitialized.
700
723
*/
701
724
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 )
703
726
{
704
727
static const char err_fmt [] = "Cannot resolve \"%s\"" ;
728
+ addrxlat_fulladdr_t * addr ;
705
729
addrxlat_status status ;
706
730
731
+ addr = & ctl -> sys -> meth [ADDRXLAT_SYS_METH_PGT ].param .pgt .root ;
707
732
if (addr -> as != ADDRXLAT_NOADDR )
708
733
return ADDRXLAT_OK ;
709
734
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 );
711
754
if (status == ADDRXLAT_OK ) {
712
755
addr -> as = ADDRXLAT_KVADDR ;
713
756
return status ;
714
757
} 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 );
717
760
718
- status = get_symval (ctx , "init_level4_pgt " , & addr -> addr );
761
+ status = get_symval (ctl -> ctx , "init_top_pgt " , & addr -> addr );
719
762
if (status == ADDRXLAT_OK ) {
720
763
addr -> as = ADDRXLAT_KVADDR ;
721
764
return status ;
722
765
} 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 );
725
768
726
- status = get_reg ( ctx , "cr3 " , & addr -> addr );
769
+ status = get_symval ( ctl -> ctx , "init_level4_pgt " , & addr -> addr );
727
770
if (status == ADDRXLAT_OK ) {
728
- addr -> addr &= ~PAGE_MASK ;
729
- addr -> as = ADDRXLAT_MACHPHYSADDR ;
771
+ addr -> as = ADDRXLAT_KVADDR ;
730
772
return status ;
731
773
} 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 );
734
776
735
777
return ADDRXLAT_OK ;
736
778
}
@@ -742,21 +784,20 @@ get_linux_pgt_root(addrxlat_ctx_t *ctx, addrxlat_fulladdr_t *addr)
742
784
static addrxlat_status
743
785
map_linux_x86_64 (struct os_init_data * ctl )
744
786
{
745
- addrxlat_meth_t * meth ;
746
787
addrxlat_addr_t sme_mask ;
747
788
unsigned long read_caps ;
748
789
addrxlat_status status ;
749
790
750
791
/* 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 );
753
793
if (status != ADDRXLAT_OK )
754
794
return set_error (ctl -> ctx , status ,
755
795
"Cannot determine root page table" );
756
796
757
797
status = get_number (ctl -> ctx , "sme_mask" , & sme_mask );
758
798
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 ;
760
801
else if (status == ADDRXLAT_ERR_NODATA )
761
802
clear_error (ctl -> ctx );
762
803
else
@@ -796,7 +837,7 @@ map_linux_x86_64(struct os_init_data *ctl)
796
837
797
838
/* Set up direct mapping. */
798
839
status = linux_directmap (ctl );
799
- if (status != ADDRXLAT_OK )
840
+ if (status != ADDRXLAT_OK && status != ADDRXLAT_ERR_NOTIMPL )
800
841
return status ;
801
842
802
843
return ADDRXLAT_OK ;
0 commit comments