@@ -574,6 +574,23 @@ fn set_fs_base_arch_prctl(fs_base: usize) -> Result<usize, litebox_common_linux:
574574 } )
575575}
576576
577+ #[ cfg( target_arch = "x86" ) ]
578+ fn set_thread_area (
579+ user_desc : litebox:: platform:: trivial_providers:: TransparentMutPtr <
580+ litebox_common_linux:: UserDesc ,
581+ > ,
582+ ) -> Result < usize , litebox_common_linux:: errno:: Errno > {
583+ unsafe { syscalls:: syscall1 ( syscalls:: Sysno :: set_thread_area, user_desc. as_usize ( ) ) } . map_err (
584+ |err| match err {
585+ syscalls:: Errno :: EFAULT => litebox_common_linux:: errno:: Errno :: EFAULT ,
586+ syscalls:: Errno :: EINVAL => litebox_common_linux:: errno:: Errno :: EINVAL ,
587+ syscalls:: Errno :: ENOSYS => litebox_common_linux:: errno:: Errno :: ENOSYS ,
588+ syscalls:: Errno :: ESRCH => litebox_common_linux:: errno:: Errno :: ESRCH ,
589+ _ => panic ! ( "unexpected error {err}" ) ,
590+ } ,
591+ )
592+ }
593+
577594pub struct PunchthroughToken {
578595 punchthrough : PunchthroughSyscall < LinuxUserland > ,
579596}
@@ -674,19 +691,7 @@ impl litebox::platform::PunchthroughToken for PunchthroughToken {
674691 }
675692 #[ cfg( target_arch = "x86" ) ]
676693 PunchthroughSyscall :: SetThreadArea { user_desc } => {
677- use litebox:: platform:: RawConstPointer as _;
678- unsafe {
679- syscalls:: syscall1 ( syscalls:: Sysno :: set_thread_area, user_desc. as_usize ( ) )
680- }
681- . map_err ( |err| {
682- litebox:: platform:: PunchthroughError :: Failure ( match err {
683- syscalls:: Errno :: EFAULT => litebox_common_linux:: errno:: Errno :: EFAULT ,
684- syscalls:: Errno :: EINVAL => litebox_common_linux:: errno:: Errno :: EINVAL ,
685- syscalls:: Errno :: ENOSYS => litebox_common_linux:: errno:: Errno :: ENOSYS ,
686- syscalls:: Errno :: ESRCH => litebox_common_linux:: errno:: Errno :: ESRCH ,
687- _ => panic ! ( "unexpected error {err}" ) ,
688- } )
689- } )
694+ set_thread_area ( user_desc) . map_err ( litebox:: platform:: PunchthroughError :: Failure )
690695 }
691696 }
692697 }
@@ -1207,12 +1212,148 @@ impl litebox::platform::SystemInfoProvider for LinuxUserland {
12071212 }
12081213}
12091214
1215+ impl LinuxUserland {
1216+ #[ cfg( target_arch = "x86_64" ) ]
1217+ fn get_thread_local_storage ( ) -> * mut litebox_common_linux:: ThreadLocalStorage < LinuxUserland > {
1218+ let tls = unsafe { litebox_common_linux:: rdgsbase ( ) } ;
1219+ if tls == 0 {
1220+ return core:: ptr:: null_mut ( ) ;
1221+ }
1222+ tls as * mut litebox_common_linux:: ThreadLocalStorage < LinuxUserland >
1223+ }
1224+
1225+ #[ cfg( target_arch = "x86" ) ]
1226+ fn get_thread_local_storage ( ) -> * mut litebox_common_linux:: ThreadLocalStorage < LinuxUserland > {
1227+ let mut fs_selector: u16 ;
1228+ unsafe {
1229+ core:: arch:: asm!(
1230+ "mov {0:x}, fs" ,
1231+ out( reg) fs_selector,
1232+ options( nostack, preserves_flags)
1233+ ) ;
1234+ }
1235+ if fs_selector == 0 {
1236+ return core:: ptr:: null_mut ( ) ;
1237+ }
1238+
1239+ let mut addr: usize ;
1240+ unsafe {
1241+ core:: arch:: asm!(
1242+ "mov {0}, fs:{offset}" ,
1243+ out( reg) addr,
1244+ offset = const core:: mem:: offset_of!( litebox_common_linux:: ThreadLocalStorage <LinuxUserland >, self_ptr) ,
1245+ options( nostack, preserves_flags)
1246+ ) ;
1247+ }
1248+ addr as * mut litebox_common_linux:: ThreadLocalStorage < LinuxUserland >
1249+ }
1250+ }
1251+
1252+ /// Similar to libc, we use fs/gs registers to store thread-local storage (TLS).
1253+ /// To avoid conflicts with libc's TLS, we choose to use gs on x86_64 and fs on x86
1254+ /// as libc uses fs on x86_64 and gs on x86.
1255+ impl litebox:: platform:: ThreadLocalStorageProvider for LinuxUserland {
1256+ type ThreadLocalStorage = litebox_common_linux:: ThreadLocalStorage < LinuxUserland > ;
1257+
1258+ #[ cfg( target_arch = "x86_64" ) ]
1259+ fn set_thread_local_storage ( & self , tls : Self :: ThreadLocalStorage ) {
1260+ let old_gs_base = unsafe { litebox_common_linux:: rdgsbase ( ) } ;
1261+ assert ! ( old_gs_base == 0 , "TLS already set for this thread" ) ;
1262+ let tls = Box :: new ( tls) ;
1263+ unsafe { litebox_common_linux:: wrgsbase ( Box :: into_raw ( tls) as usize ) } ;
1264+ }
1265+
1266+ #[ cfg( target_arch = "x86" ) ]
1267+ fn set_thread_local_storage ( & self , tls : Self :: ThreadLocalStorage ) {
1268+ let mut old_fs_selector: u16 ;
1269+ unsafe {
1270+ core:: arch:: asm!(
1271+ "mov {0:x}, fs" ,
1272+ out( reg) old_fs_selector,
1273+ options( nostack, preserves_flags)
1274+ ) ;
1275+ }
1276+ assert ! ( old_fs_selector == 0 , "TLS already set for this thread" ) ;
1277+
1278+ let mut tls = Box :: new ( tls) ;
1279+ tls. self_ptr = tls. as_mut ( ) ;
1280+
1281+ let mut flags = litebox_common_linux:: UserDescFlags ( 0 ) ;
1282+ flags. set_seg_32bit ( true ) ;
1283+ flags. set_useable ( true ) ;
1284+ let mut user_desc = litebox_common_linux:: UserDesc {
1285+ entry_number : u32:: MAX ,
1286+ base_addr : Box :: into_raw ( tls) as u32 ,
1287+ limit : u32:: try_from ( core:: mem:: size_of :: < Self :: ThreadLocalStorage > ( ) ) . unwrap ( ) - 1 ,
1288+ flags,
1289+ } ;
1290+ let user_desc_ptr = litebox:: platform:: trivial_providers:: TransparentMutPtr {
1291+ inner : & raw mut user_desc,
1292+ } ;
1293+ set_thread_area ( user_desc_ptr) . expect ( "Failed to set thread area for TLS" ) ;
1294+
1295+ let new_fs_selector = ( ( user_desc. entry_number & 0xfff ) << 3 ) | 0x3 ; // user mode
1296+ // set fs selector
1297+ unsafe {
1298+ core:: arch:: asm!(
1299+ "mov fs, {0:x}" ,
1300+ in( reg) new_fs_selector,
1301+ options( nostack, preserves_flags)
1302+ ) ;
1303+ }
1304+ }
1305+
1306+ #[ cfg( target_arch = "x86_64" ) ]
1307+ fn release_thread_local_storage ( & self ) -> Self :: ThreadLocalStorage {
1308+ let tls = Self :: get_thread_local_storage ( ) ;
1309+ assert ! ( !tls. is_null( ) , "TLS must be set before releasing it" ) ;
1310+ unsafe {
1311+ litebox_common_linux:: wrgsbase ( 0 ) ;
1312+ }
1313+
1314+ let tls = unsafe { Box :: from_raw ( tls) } ;
1315+ assert ! ( !tls. borrowed, "TLS must not be borrowed when releasing it" ) ;
1316+ * tls
1317+ }
1318+
1319+ #[ cfg( target_arch = "x86" ) ]
1320+ fn release_thread_local_storage ( & self ) -> Self :: ThreadLocalStorage {
1321+ let tls = Self :: get_thread_local_storage ( ) ;
1322+ assert ! ( !tls. is_null( ) , "TLS must be set before releasing it" ) ;
1323+ unsafe {
1324+ core:: arch:: asm!(
1325+ "mov fs, {0}" ,
1326+ in( reg) 0 ,
1327+ options( nostack, preserves_flags)
1328+ ) ;
1329+ }
1330+
1331+ let tls = unsafe { Box :: from_raw ( tls) } ;
1332+ assert ! ( !tls. borrowed, "TLS must not be borrowed when releasing it" ) ;
1333+ * tls
1334+ }
1335+
1336+ fn with_thread_local_storage_mut < F , R > ( & self , f : F ) -> R
1337+ where
1338+ F : FnOnce ( & mut Self :: ThreadLocalStorage ) -> R ,
1339+ {
1340+ let tls = Self :: get_thread_local_storage ( ) ;
1341+ assert ! ( !tls. is_null( ) , "TLS must be set before accessing it" ) ;
1342+ let tls = unsafe { & mut * tls } ;
1343+ assert ! ( !tls. borrowed, "TLS is already borrowed" ) ;
1344+ tls. borrowed = true ; // mark as borrowed
1345+ let ret = f ( tls) ;
1346+ tls. borrowed = false ; // mark as not borrowed anymore
1347+ ret
1348+ }
1349+ }
1350+
12101351#[ cfg( test) ]
12111352mod tests {
12121353 use core:: sync:: atomic:: AtomicU32 ;
12131354 use std:: thread:: sleep;
12141355
1215- use litebox:: platform:: RawMutex ;
1356+ use litebox:: platform:: { RawMutex , ThreadLocalStorageProvider as _ } ;
12161357
12171358 use crate :: LinuxUserland ;
12181359 use litebox:: platform:: PageManagementProvider ;
@@ -1249,4 +1390,29 @@ mod tests {
12491390 prev = page. end ;
12501391 }
12511392 }
1393+
1394+ #[ test]
1395+ fn test_tls ( ) {
1396+ let platform = LinuxUserland :: new ( None ) ;
1397+ let tls = LinuxUserland :: get_thread_local_storage ( ) ;
1398+ assert ! ( tls. is_null( ) , "TLS should be null in the main thread" ) ;
1399+ platform. set_thread_local_storage ( litebox_common_linux:: ThreadLocalStorage :: new ( Box :: new (
1400+ litebox_common_linux:: Task { tid : 0xffff } ,
1401+ ) ) ) ;
1402+ platform. with_thread_local_storage_mut ( |tls| {
1403+ assert_eq ! (
1404+ tls. current_task. tid, 0xffff ,
1405+ "TLS should have the correct task ID"
1406+ ) ;
1407+ tls. current_task . tid = 0x1234 ; // Change the task ID
1408+ } ) ;
1409+ let tls = platform. release_thread_local_storage ( ) ;
1410+ assert_eq ! (
1411+ tls. current_task. tid, 0x1234 ,
1412+ "TLS should have the correct task ID"
1413+ ) ;
1414+
1415+ let tls = LinuxUserland :: get_thread_local_storage ( ) ;
1416+ assert ! ( tls. is_null( ) , "TLS should be null after releasing it" ) ;
1417+ }
12521418}
0 commit comments