chiark / gitweb /
Fix handling of cpuid and PIC on i386 systems
[stressapptest] / src / os.cc
1 // Copyright 2006 Google Inc. All Rights Reserved.
2 // Author: nsanders, menderico
3
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7
8 //      http://www.apache.org/licenses/LICENSE-2.0
9
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 // os.cc : os and machine specific implementation
17 // This file includes an abstracted interface
18 // for linux-distro specific and HW specific
19 // interfaces.
20
21 #include "os.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <linux/types.h>
26 #include <malloc.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/ioctl.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/ipc.h>
35 #ifdef HAVE_SYS_SHM_H
36 #include <sys/shm.h>
37 #endif
38 #include <unistd.h>
39
40 #ifndef SHM_HUGETLB
41 #define SHM_HUGETLB      04000  // remove when glibc defines it
42 #endif
43
44 #include <string>
45 #include <list>
46
47 // This file must work with autoconf on its public version,
48 // so these includes are correct.
49 #include "sattypes.h"
50 #include "error_diag.h"
51
52 // OsLayer initialization.
53 OsLayer::OsLayer() {
54   testmem_ = 0;
55   testmemsize_ = 0;
56   totalmemsize_ = 0;
57   min_hugepages_bytes_ = 0;
58   normal_mem_ = true;
59   use_hugepages_ = false;
60   use_posix_shm_ = false;
61   dynamic_mapped_shmem_ = false;
62   shmid_ = 0;
63
64   time_initialized_ = 0;
65
66   regionsize_ = 0;
67   regioncount_ = 1;
68   num_cpus_ = 0;
69   num_nodes_ = 0;
70   num_cpus_per_node_ = 0;
71   error_diagnoser_ = 0;
72   err_log_callback_ = 0;
73   error_injection_ = false;
74
75   void *pvoid = 0;
76   address_mode_ = sizeof(pvoid) * 8;
77
78   has_clflush_ = false;
79   has_sse2_ = false;
80
81   use_flush_page_cache_ = false;
82 }
83
84 // OsLayer cleanup.
85 OsLayer::~OsLayer() {
86   if (error_diagnoser_)
87     delete error_diagnoser_;
88 }
89
90 // OsLayer initialization.
91 bool OsLayer::Initialize() {
92   time_initialized_ = time(NULL);
93   // Detect asm support.
94   GetFeatures();
95
96   if (num_cpus_ == 0) {
97     num_nodes_ = 1;
98     num_cpus_ = sysconf(_SC_NPROCESSORS_ONLN);
99     num_cpus_per_node_ = num_cpus_ / num_nodes_;
100   }
101   logprintf(5, "Log: %d nodes, %d cpus.\n", num_nodes_, num_cpus_);
102   sat_assert(CPU_SETSIZE >= num_cpus_);
103   cpu_sets_.resize(num_nodes_);
104   cpu_sets_valid_.resize(num_nodes_);
105   // Create error diagnoser.
106   error_diagnoser_ = new ErrorDiag();
107   if (!error_diagnoser_->set_os(this))
108     return false;
109   return true;
110 }
111
112 // Machine type detected. Can we implement all these functions correctly?
113 bool OsLayer::IsSupported() {
114   if (kOpenSource) {
115     // There are no explicitly supported systems in open source version.
116     return true;
117   }
118
119   // This is the default empty implementation.
120   // SAT won't report full error information.
121   return false;
122 }
123
124 int OsLayer::AddressMode() {
125   // Detect 32/64 bit binary.
126   void *pvoid = 0;
127   return sizeof(pvoid) * 8;
128 }
129
130 // Translates user virtual to physical address.
131 uint64 OsLayer::VirtualToPhysical(void *vaddr) {
132   uint64 frame, shift;
133   off64_t off = ((uintptr_t)vaddr) / getpagesize() * 8;
134   int fd = open(kPagemapPath, O_RDONLY);
135   if (fd < 0 || lseek64(fd, off, SEEK_SET) != off || read(fd, &frame, 8) != 8) {
136     int err = errno;
137     string errtxt = ErrorString(err);
138     logprintf(0, "Error: failed to access %s with errno %d (%s)\n",
139               kPagemapPath, err, errtxt.c_str());
140     if (fd >= 0)
141       close(fd);
142     return 0;
143   }
144   close(fd);
145   if (!(frame & (1LL << 63)) || (frame & (1LL << 62)))
146     return 0;
147   shift = (frame >> 55) & 0x3f;
148   frame = (frame & 0x007fffffffffffffLL) << shift;
149   return frame | ((uintptr_t)vaddr & ((1LL << shift) - 1));
150 }
151
152 // Returns the HD device that contains this file.
153 string OsLayer::FindFileDevice(string filename) {
154   return "hdUnknown";
155 }
156
157 // Returns a list of locations corresponding to HD devices.
158 list<string> OsLayer::FindFileDevices() {
159   // No autodetection on unknown systems.
160   list<string> locations;
161   return locations;
162 }
163
164
165 // Get HW core features from cpuid instruction.
166 void OsLayer::GetFeatures() {
167 #if defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
168   // CPUID features documented at:
169   // http://www.sandpile.org/ia32/cpuid.htm
170   int ax, bx, cx, dx;
171   __asm__ __volatile__ (
172 # if defined(STRESSAPPTEST_CPU_I686) && defined(__PIC__)
173       "xchg %%ebx, %%esi;"
174       "cpuid;"
175       "xchg %%esi, %%ebx;"
176       : "=S" (bx),
177 # else
178       "cpuid;"
179       : "=b" (bx),
180 # endif
181         "=a" (ax), "=c" (cx), "=d" (dx) : "a" (1));
182   has_clflush_ = (dx >> 19) & 1;
183   has_sse2_ = (dx >> 26) & 1;
184
185   logprintf(9, "Log: has clflush: %s, has sse2: %s\n",
186             has_clflush_ ? "true" : "false",
187             has_sse2_ ? "true" : "false");
188 #elif defined(STRESSAPPTEST_CPU_PPC)
189   // All PPC implementations have cache flush instructions.
190   has_clflush_ = true;
191 #elif defined(STRESSAPPTEST_CPU_ARMV7A)
192 #warning "Unsupported CPU type ARMV7A: unable to determine feature set."
193 #else
194 #warning "Unsupported CPU type: unable to determine feature set."
195 #endif
196 }
197
198
199 // Enable FlushPageCache to be functional instead of a NOP.
200 void OsLayer::ActivateFlushPageCache(void) {
201   logprintf(9, "Log: page cache will be flushed as needed\n");
202   use_flush_page_cache_ = true;
203 }
204
205 // Flush the page cache to ensure reads come from the disk.
206 bool OsLayer::FlushPageCache(void) {
207   if (!use_flush_page_cache_)
208     return true;
209
210   // First, ask the kernel to write the cache to the disk.
211   sync();
212
213   // Second, ask the kernel to empty the cache by writing "1" to
214   // "/proc/sys/vm/drop_caches".
215   static const char *drop_caches_file = "/proc/sys/vm/drop_caches";
216   int dcfile = open(drop_caches_file, O_WRONLY);
217   if (dcfile < 0) {
218     int err = errno;
219     string errtxt = ErrorString(err);
220     logprintf(3, "Log: failed to open %s - err %d (%s)\n",
221               drop_caches_file, err, errtxt.c_str());
222     return false;
223   }
224
225   ssize_t bytes_written = write(dcfile, "1", 1);
226   close(dcfile);
227
228   if (bytes_written != 1) {
229     int err = errno;
230     string errtxt = ErrorString(err);
231     logprintf(3, "Log: failed to write %s - err %d (%s)\n",
232               drop_caches_file, err, errtxt.c_str());
233     return false;
234   }
235   return true;
236 }
237
238
239 // We need to flush the cacheline here.
240 void OsLayer::Flush(void *vaddr) {
241   // Use the generic flush. This function is just so we can override
242   // this if we are so inclined.
243   if (has_clflush_)
244     FastFlush(vaddr);
245 }
246
247
248 // Run C or ASM copy as appropriate..
249 bool OsLayer::AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem,
250                               unsigned int size_in_bytes,
251                               AdlerChecksum *checksum) {
252   if (has_sse2_) {
253     return AdlerMemcpyAsm(dstmem, srcmem, size_in_bytes, checksum);
254   } else {
255     return AdlerMemcpyWarmC(dstmem, srcmem, size_in_bytes, checksum);
256   }
257 }
258
259
260 // Translate user virtual to physical address.
261 int OsLayer::FindDimm(uint64 addr, char *buf, int len) {
262   char tmpbuf[256];
263   snprintf(tmpbuf, sizeof(tmpbuf), "DIMM Unknown");
264   snprintf(buf, len, "%s", tmpbuf);
265   return 0;
266 }
267
268
269 // Classifies addresses according to "regions"
270 // This isn't really implemented meaningfully here..
271 int32 OsLayer::FindRegion(uint64 addr) {
272   static bool warned = false;
273
274   if (regionsize_ == 0) {
275     regionsize_ = totalmemsize_ / 8;
276     if (regionsize_ < 512 * kMegabyte)
277       regionsize_ = 512 * kMegabyte;
278     regioncount_ = totalmemsize_ / regionsize_;
279     if (regioncount_ < 1) regioncount_ = 1;
280   }
281
282   int32 region_num = addr / regionsize_;
283   if (region_num >= regioncount_) {
284     if (!warned) {
285         logprintf(0, "Log: region number %d exceeds region count %d\n",
286                   region_num, regioncount_);
287         warned = true;
288     }
289     region_num = region_num % regioncount_;
290   }
291   return region_num;
292 }
293
294 // Report which cores are associated with a given region.
295 cpu_set_t *OsLayer::FindCoreMask(int32 region) {
296   sat_assert(region >= 0);
297   region %= num_nodes_;
298   if (!cpu_sets_valid_[region]) {
299     CPU_ZERO(&cpu_sets_[region]);
300     for (int i = 0; i < num_cpus_per_node_; ++i) {
301       CPU_SET(i + region * num_cpus_per_node_, &cpu_sets_[region]);
302     }
303     cpu_sets_valid_[region] = true;
304     logprintf(5, "Log: Region %d mask 0x%s\n",
305                  region, FindCoreMaskFormat(region).c_str());
306   }
307   return &cpu_sets_[region];
308 }
309
310 // Return cores associated with a given region in hex string.
311 string OsLayer::FindCoreMaskFormat(int32 region) {
312   cpu_set_t* mask = FindCoreMask(region);
313   string format = cpuset_format(mask);
314   if (format.size() < 8)
315     format = string(8 - format.size(), '0') + format;
316   return format;
317 }
318
319 // Report an error in an easily parseable way.
320 bool OsLayer::ErrorReport(const char *part, const char *symptom, int count) {
321   time_t now = time(NULL);
322   int ttf = now - time_initialized_;
323   logprintf(0, "Report Error: %s : %s : %d : %ds\n", symptom, part, count, ttf);
324   return true;
325 }
326
327 // Read the number of hugepages out of the kernel interface in proc.
328 int64 OsLayer::FindHugePages() {
329   char buf[65] = "0";
330
331   // This is a kernel interface to query the numebr of hugepages
332   // available in the system.
333   static const char *hugepages_info_file = "/proc/sys/vm/nr_hugepages";
334   int hpfile = open(hugepages_info_file, O_RDONLY);
335
336   ssize_t bytes_read = read(hpfile, buf, 64);
337   close(hpfile);
338
339   if (bytes_read <= 0) {
340     logprintf(12, "Log: /proc/sys/vm/nr_hugepages "
341                   "read did not provide data\n");
342     return 0;
343   }
344
345   if (bytes_read == 64) {
346     logprintf(0, "Process Error: /proc/sys/vm/nr_hugepages "
347                  "is surprisingly large\n");
348     return 0;
349   }
350
351   // Add a null termintation to be string safe.
352   buf[bytes_read] = '\0';
353   // Read the page count.
354   int64 pages = strtoull(buf, NULL, 10);  // NOLINT
355
356   return pages;
357 }
358
359 int64 OsLayer::FindFreeMemSize() {
360   int64 size = 0;
361   int64 minsize = 0;
362   if (totalmemsize_ > 0)
363     return totalmemsize_;
364
365   int64 pages = sysconf(_SC_PHYS_PAGES);
366   int64 avpages = sysconf(_SC_AVPHYS_PAGES);
367   int64 pagesize = sysconf(_SC_PAGESIZE);
368   int64 physsize = pages * pagesize;
369   int64 avphyssize = avpages * pagesize;
370
371   // Assume 2MB hugepages.
372   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
373
374   if ((pages == -1) || (pagesize == -1)) {
375     logprintf(0, "Process Error: sysconf could not determine memory size.\n");
376     return 0;
377   }
378
379   // We want to leave enough stuff for things to run.
380   // If the user specified a minimum amount of memory to expect, require that.
381   // Otherwise, if more than 2GB is present, leave 192M + 5% for other stuff.
382   // If less than 2GB is present use 85% of what's available.
383   // These are fairly arbitrary numbers that seem to work OK.
384   //
385   // TODO(nsanders): is there a more correct way to determine target
386   // memory size?
387   if (hugepagesize > 0 && min_hugepages_bytes_ > 0) {
388     minsize = min_hugepages_bytes_;
389   } else if (physsize < 2048LL * kMegabyte) {
390     minsize = ((pages * 85) / 100) * pagesize;
391   } else {
392     minsize = ((pages * 95) / 100) * pagesize - (192 * kMegabyte);
393   }
394
395   // Use hugepage sizing if available.
396   if (hugepagesize > 0) {
397     if (hugepagesize < minsize) {
398       logprintf(0, "Procedural Error: Not enough hugepages. "
399                    "%lldMB available < %lldMB required.\n",
400                 hugepagesize / kMegabyte,
401                 minsize / kMegabyte);
402       // Require the calculated minimum amount of memory.
403       size = minsize;
404     } else {
405       // Require that we get all hugepages.
406       size = hugepagesize;
407     }
408   } else {
409     // Require the calculated minimum amount of memory.
410     size = minsize;
411   }
412
413   logprintf(5, "Log: Total %lld MB. Free %lld MB. Hugepages %lld MB. "
414                "Targeting %lld MB (%lld%%)\n",
415             physsize / kMegabyte,
416             avphyssize / kMegabyte,
417             hugepagesize / kMegabyte,
418             size / kMegabyte,
419             size * 100 / physsize);
420
421   totalmemsize_ = size;
422   return size;
423 }
424
425 // Allocates all memory available.
426 int64 OsLayer::AllocateAllMem() {
427   int64 length = FindFreeMemSize();
428   bool retval = AllocateTestMem(length, 0);
429   if (retval)
430     return length;
431   else
432     return 0;
433 }
434
435 // Allocate the target memory. This may be from malloc, hugepage pool
436 // or other platform specific sources.
437 bool OsLayer::AllocateTestMem(int64 length, uint64 paddr_base) {
438   // Try hugepages first.
439   void *buf = 0;
440
441   sat_assert(length >= 0);
442
443   if (paddr_base)
444     logprintf(0, "Process Error: non zero paddr_base %#llx is not supported,"
445               " ignore.\n", paddr_base);
446
447   // Determine optimal memory allocation path.
448   bool prefer_hugepages = false;
449   bool prefer_posix_shm = false;
450   bool prefer_dynamic_mapping = false;
451
452   // Are there enough hugepages?
453   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
454   // TODO(nsanders): Is there enough /dev/shm? Is there enough free memeory?
455   if ((length >= 1400LL * kMegabyte) && (address_mode_ == 32)) {
456     prefer_dynamic_mapping = true;
457     prefer_posix_shm = true;
458     logprintf(3, "Log: Prefer POSIX shared memory allocation.\n");
459     logprintf(3, "Log: You may need to run "
460                  "'sudo mount -o remount,size=100\% /dev/shm.'\n");
461   } else if (hugepagesize >= length) {
462     prefer_hugepages = true;
463     logprintf(3, "Log: Prefer using hugepace allocation.\n");
464   } else {
465     logprintf(3, "Log: Prefer plain malloc memory allocation.\n");
466   }
467
468 #ifdef HAVE_SYS_SHM_H
469   // Allocate hugepage mapped memory.
470   if (prefer_hugepages) {
471     do { // Allow break statement.
472       int shmid;
473       void *shmaddr;
474
475       if ((shmid = shmget(2, length,
476               SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) {
477         int err = errno;
478         string errtxt = ErrorString(err);
479         logprintf(3, "Log: failed to allocate shared hugepage "
480                       "object - err %d (%s)\n",
481                   err, errtxt.c_str());
482         logprintf(3, "Log: sysctl -w vm.nr_hugepages=XXX allows hugepages.\n");
483         break;
484       }
485
486       shmaddr = shmat(shmid, NULL, NULL);
487       if (shmaddr == reinterpret_cast<void*>(-1)) {
488         int err = errno;
489         string errtxt = ErrorString(err);
490         logprintf(0, "Log: failed to attach shared "
491                      "hugepage object - err %d (%s).\n",
492                   err, errtxt.c_str());
493         if (shmctl(shmid, IPC_RMID, NULL) < 0) {
494           int err = errno;
495           string errtxt = ErrorString(err);
496           logprintf(0, "Log: failed to remove shared "
497                        "hugepage object - err %d (%s).\n",
498                     err, errtxt.c_str());
499         }
500         break;
501       }
502       use_hugepages_ = true;
503       shmid_ = shmid;
504       buf = shmaddr;
505       logprintf(0, "Log: Using shared hugepage object 0x%x at %p.\n",
506                 shmid, shmaddr);
507     } while (0);
508   }
509
510   if ((!use_hugepages_) && prefer_posix_shm) {
511     do {
512       int shm_object;
513       void *shmaddr = NULL;
514
515       shm_object = shm_open("/stressapptest", O_CREAT | O_RDWR, S_IRWXU);
516       if (shm_object < 0) {
517         int err = errno;
518         string errtxt = ErrorString(err);
519         logprintf(3, "Log: failed to allocate shared "
520                       "smallpage object - err %d (%s)\n",
521                   err, errtxt.c_str());
522         break;
523       }
524
525       if (0 > ftruncate(shm_object, length)) {
526         int err = errno;
527         string errtxt = ErrorString(err);
528         logprintf(3, "Log: failed to ftruncate shared "
529                       "smallpage object - err %d (%s)\n",
530                   err, errtxt.c_str());
531         break;
532       }
533
534       // 32 bit linux apps can only use ~1.4G of address space.
535       // Use dynamic mapping for allocations larger than that.
536       // Currently perf hit is ~10% for this.
537       if (prefer_dynamic_mapping) {
538         dynamic_mapped_shmem_ = true;
539       } else {
540         // Do a full mapping here otherwise.
541         shmaddr = mmap64(NULL, length, PROT_READ | PROT_WRITE,
542                          MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
543                          shm_object, NULL);
544         if (shmaddr == reinterpret_cast<void*>(-1)) {
545           int err = errno;
546           string errtxt = ErrorString(err);
547           logprintf(0, "Log: failed to map shared "
548                        "smallpage object - err %d (%s).\n",
549                     err, errtxt.c_str());
550           break;
551         }
552       }
553
554       use_posix_shm_ = true;
555       shmid_ = shm_object;
556       buf = shmaddr;
557       char location_message[256] = "";
558       if (dynamic_mapped_shmem_) {
559         sprintf(location_message, "mapped as needed");
560       } else {
561         sprintf(location_message, "at %p", shmaddr);
562       }
563       logprintf(0, "Log: Using posix shared memory object 0x%x %s.\n",
564                 shm_object, location_message);
565     } while (0);
566     shm_unlink("/stressapptest");
567   }
568 #endif // HAVE_SYS_SHM_H
569
570   if (!use_hugepages_ && !use_posix_shm_) {
571     // Use memalign to ensure that blocks are aligned enough for disk direct IO.
572     buf = static_cast<char*>(memalign(4096, length));
573     if (buf) {
574       logprintf(0, "Log: Using memaligned allocation at %p.\n", buf);
575     } else {
576       logprintf(0, "Process Error: memalign returned 0\n");
577       if ((length >= 1499LL * kMegabyte) && (address_mode_ == 32)) {
578         logprintf(0, "Log: You are trying to allocate > 1.4G on a 32 "
579                      "bit process. Please setup shared memory.\n");
580       }
581     }
582   }
583
584   testmem_ = buf;
585   if (buf || dynamic_mapped_shmem_) {
586     testmemsize_ = length;
587   } else {
588     testmemsize_ = 0;
589   }
590
591   return (buf != 0) || dynamic_mapped_shmem_;
592 }
593
594 // Free the test memory.
595 void OsLayer::FreeTestMem() {
596   if (testmem_) {
597     if (use_hugepages_) {
598 #ifdef HAVE_SYS_SHM_H
599       shmdt(testmem_);
600       shmctl(shmid_, IPC_RMID, NULL);
601 #endif
602     } else if (use_posix_shm_) {
603       if (!dynamic_mapped_shmem_) {
604         munmap(testmem_, testmemsize_);
605       }
606       close(shmid_);
607     } else {
608       free(testmem_);
609     }
610     testmem_ = 0;
611     testmemsize_ = 0;
612   }
613 }
614
615
616 // Prepare the target memory. It may requre mapping in, or this may be a noop.
617 void *OsLayer::PrepareTestMem(uint64 offset, uint64 length) {
618   sat_assert((offset + length) <= testmemsize_);
619   if (dynamic_mapped_shmem_) {
620     // TODO(nsanders): Check if we can support MAP_NONBLOCK,
621     // and evaluate performance hit from not using it.
622 #ifdef HAVE_MMAP64
623     void * mapping = mmap64(NULL, length, PROT_READ | PROT_WRITE,
624                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
625                      shmid_, offset);
626 #else
627     void * mapping = mmap(NULL, length, PROT_READ | PROT_WRITE,
628                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
629                      shmid_, offset);
630 #endif
631     if (mapping == MAP_FAILED) {
632       string errtxt = ErrorString(errno);
633       logprintf(0, "Process Error: PrepareTestMem mmap64(%llx, %llx) failed. "
634                    "error: %s.\n",
635                 offset, length, errtxt.c_str());
636       sat_assert(0);
637     }
638     return mapping;
639   }
640
641   return reinterpret_cast<void*>(reinterpret_cast<char*>(testmem_) + offset);
642 }
643
644 // Release the test memory resources, if any.
645 void OsLayer::ReleaseTestMem(void *addr, uint64 offset, uint64 length) {
646   if (dynamic_mapped_shmem_) {
647     int retval = munmap(addr, length);
648     if (retval == -1) {
649       string errtxt = ErrorString(errno);
650       logprintf(0, "Process Error: ReleaseTestMem munmap(%p, %llx) failed. "
651                    "error: %s.\n",
652                 addr, length, errtxt.c_str());
653       sat_assert(0);
654     }
655   }
656 }
657
658 // No error polling on unknown systems.
659 int OsLayer::ErrorPoll() {
660   return 0;
661 }
662
663 // Generally, poll for errors once per second.
664 void OsLayer::ErrorWait() {
665   sat_sleep(1);
666   return;
667 }
668
669 // Open a PCI bus-dev-func as a file and return its file descriptor.
670 // Error is indicated by return value less than zero.
671 int OsLayer::PciOpen(int bus, int device, int function) {
672   char dev_file[256];
673
674   snprintf(dev_file, sizeof(dev_file), "/proc/bus/pci/%02x/%02x.%x",
675            bus, device, function);
676
677   int fd = open(dev_file, O_RDWR);
678   if (fd == -1) {
679     logprintf(0, "Process Error: Unable to open PCI bus %d, device %d, "
680                  "function %d (errno %d).\n",
681               bus, device, function, errno);
682     return -1;
683   }
684
685   return fd;
686 }
687
688
689 // Read and write functions to access PCI config.
690 uint32 OsLayer::PciRead(int fd, uint32 offset, int width) {
691   // Strict aliasing rules lawyers will cause data corruption
692   // on cast pointers in some gccs.
693   union {
694     uint32 l32;
695     uint16 l16;
696     uint8 l8;
697   } datacast;
698   datacast.l32 = 0;
699   uint32 size = width / 8;
700
701   sat_assert((width == 32) || (width == 16) || (width == 8));
702   sat_assert(offset <= (256 - size));
703
704   if (lseek(fd, offset, SEEK_SET) < 0) {
705     logprintf(0, "Process Error: Can't seek %x\n", offset);
706     return 0;
707   }
708   if (read(fd, &datacast, size) != static_cast<ssize_t>(size)) {
709     logprintf(0, "Process Error: Can't read %x\n", offset);
710     return 0;
711   }
712
713   // Extract the data.
714   switch (width) {
715     case 8:
716       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
717       return datacast.l8;
718     case 16:
719       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
720       return datacast.l16;
721     case 32:
722       return datacast.l32;
723   }
724   return 0;
725 }
726
727 void OsLayer::PciWrite(int fd, uint32 offset, uint32 value, int width) {
728   // Strict aliasing rules lawyers will cause data corruption
729   // on cast pointers in some gccs.
730   union {
731     uint32 l32;
732     uint16 l16;
733     uint8 l8;
734   } datacast;
735   datacast.l32 = 0;
736   uint32 size = width / 8;
737
738   sat_assert((width == 32) || (width == 16) || (width == 8));
739   sat_assert(offset <= (256 - size));
740
741   // Cram the data into the right alignment.
742   switch (width) {
743     case 8:
744       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
745       datacast.l8 = value;
746     case 16:
747       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
748       datacast.l16 = value;
749     case 32:
750       datacast.l32 = value;
751   }
752
753   if (lseek(fd, offset, SEEK_SET) < 0) {
754     logprintf(0, "Process Error: Can't seek %x\n", offset);
755     return;
756   }
757   if (write(fd, &datacast, size) != static_cast<ssize_t>(size)) {
758     logprintf(0, "Process Error: Can't write %x to %x\n", datacast.l32, offset);
759     return;
760   }
761
762   return;
763 }
764
765
766
767 // Open dev msr.
768 int OsLayer::OpenMSR(uint32 core, uint32 address) {
769   char buf[256];
770   snprintf(buf, sizeof(buf), "/dev/cpu/%d/msr", core);
771   int fd = open(buf, O_RDWR);
772   if (fd < 0)
773     return fd;
774
775   uint32 pos = lseek(fd, address, SEEK_SET);
776   if (pos != address) {
777     close(fd);
778     logprintf(5, "Log: can't seek to msr %x, cpu %d\n", address, core);
779     return -1;
780   }
781
782   return fd;
783 }
784
785 bool OsLayer::ReadMSR(uint32 core, uint32 address, uint64 *data) {
786   int fd = OpenMSR(core, address);
787   if (fd < 0)
788     return false;
789
790   // Read from the msr.
791   bool res = (sizeof(*data) == read(fd, data, sizeof(*data)));
792
793   if (!res)
794     logprintf(5, "Log: Failed to read msr %x core %d\n", address, core);
795
796   close(fd);
797
798   return res;
799 }
800
801 bool OsLayer::WriteMSR(uint32 core, uint32 address, uint64 *data) {
802   int fd = OpenMSR(core, address);
803   if (fd < 0)
804     return false;
805
806   // Write to the msr
807   bool res = (sizeof(*data) == write(fd, data, sizeof(*data)));
808
809   if (!res)
810     logprintf(5, "Log: Failed to write msr %x core %d\n", address, core);
811
812   close(fd);
813
814   return res;
815 }
816
817 // Extract bits [n+len-1, n] from a 32 bit word.
818 // so GetBitField(0x0f00, 8, 4) == 0xf.
819 uint32 OsLayer::GetBitField(uint32 val, uint32 n, uint32 len) {
820   return (val >> n) & ((1<<len) - 1);
821 }
822
823 // Generic CPU stress workload that would work on any CPU/Platform.
824 // Float-point array moving average calculation.
825 bool OsLayer::CpuStressWorkload() {
826   double float_arr[100];
827   double sum = 0;
828   unsigned int seed = 12345;
829
830   // Initialize array with random numbers.
831   for (int i = 0; i < 100; i++) {
832 #ifdef HAVE_RAND_R
833     float_arr[i] = rand_r(&seed);
834     if (rand_r(&seed) % 2)
835       float_arr[i] *= -1.0;
836 #else
837     float_arr[i] = rand();
838     if (rand() % 2)
839       float_arr[i] *= -1.0;
840 #endif
841   }
842
843   // Calculate moving average.
844   for (int i = 0; i < 100000000; i++) {
845     float_arr[i % 100] =
846       (float_arr[i % 100] + float_arr[(i + 1) % 100] +
847        float_arr[(i + 99) % 100]) / 3;
848     sum += float_arr[i % 100];
849   }
850
851   // Artificial printf so the loops do not get optimized away.
852   if (sum == 0.0)
853     logprintf(12, "Log: I'm Feeling Lucky!\n");
854   return true;
855 }
856
857 PCIDevices OsLayer::GetPCIDevices() {
858   PCIDevices device_list;
859   DIR *dir;
860   struct dirent *buf = new struct dirent();
861   struct dirent *entry;
862   dir = opendir(kSysfsPath);
863   if (!dir)
864     logprintf(0, "Process Error: Cannot open %s", kSysfsPath);
865   while (readdir_r(dir, buf, &entry) == 0 && entry) {
866     PCIDevice *device;
867     unsigned int dev, func;
868     // ".", ".." or a special non-device perhaps.
869     if (entry->d_name[0] == '.')
870       continue;
871
872     device = new PCIDevice();
873     if (sscanf(entry->d_name, "%04x:%02hx:%02x.%d",
874                &device->domain, &device->bus, &dev, &func) < 4) {
875       logprintf(0, "Process Error: Couldn't parse %s", entry->d_name);
876       free(device);
877       continue;
878     }
879     device->dev = dev;
880     device->func = func;
881     device->vendor_id = PCIGetValue(entry->d_name, "vendor");
882     device->device_id = PCIGetValue(entry->d_name, "device");
883     PCIGetResources(entry->d_name, device);
884     device_list.insert(device_list.end(), device);
885   }
886   closedir(dir);
887   delete buf;
888   return device_list;
889 }
890
891 int OsLayer::PCIGetValue(string name, string object) {
892   int fd, len;
893   char filename[256];
894   char buf[256];
895   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
896            name.c_str(), object.c_str());
897   fd = open(filename, O_RDONLY);
898   if (fd < 0)
899     return 0;
900   len = read(fd, buf, 256);
901   close(fd);
902   buf[len] = '\0';
903   return strtol(buf, NULL, 0);  // NOLINT
904 }
905
906 int OsLayer::PCIGetResources(string name, PCIDevice *device) {
907   char filename[256];
908   char buf[256];
909   FILE *file;
910   int64 start;
911   int64 end;
912   int64 size;
913   int i;
914   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
915            name.c_str(), "resource");
916   file = fopen(filename, "r");
917   if (!file) {
918     logprintf(0, "Process Error: impossible to find resource file for %s",
919               filename);
920     return errno;
921   }
922   for (i = 0; i < 6; i++) {
923     if (!fgets(buf, 256, file))
924       break;
925     sscanf(buf, "%llx %llx", &start, &end);  // NOLINT
926     size = 0;
927     if (start)
928       size = end - start + 1;
929     device->base_addr[i] = start;
930     device->size[i] = size;
931   }
932   fclose(file);
933   return 0;
934 }