Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
#!/usr/bin/env python3

import os
import sys
import struct
import parser
from collections import namedtuple
import ctypes
import argparse
import re
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection

#  global variables
pd_complete = ''
inputfile = ''
outputfile = ''
list_of_pde = {}
num_of_regions = 0
read_buff = ''
raw_info = []

mmu_region_details = namedtuple("mmu_region_details",
                                "pde_index page_entries_info")

valid_pages_inside_pde = namedtuple("valid_pages_inside_pde", "start_addr size \
                                pte_valid_addr_start \
                                pte_valid_addr_end \
                                permissions")

mmu_region_details_pdpt = namedtuple("mmu_region_details_pdpt",
                                     "pdpte_index pd_entries")

page_tables_list = []
pd_tables_list = []
pd_start_addr = 0
validation_issue_memory_overlap = [False, 0, -1]
output_offset = 0
print_string_pde_list = ''
pde_pte_string = {}
FourMB = (1024 * 4096)  # In Bytes

#  Constants
PAGE_ENTRY_PRESENT = 1
PAGE_ENTRY_READ_WRITE = 1 << 1
PAGE_ENTRY_USER_SUPERVISOR = 1 << 2
PAGE_ENTRY_PWT = 0 << 3
PAGE_ENTRY_PCD = 0 << 4
PAGE_ENTRY_ACCESSED = 0 << 5  # this is a read only field
PAGE_ENTRY_DIRTY = 0 << 6  # this is a read only field
PAGE_ENTRY_PAT = 0 << 7
PAGE_ENTRY_GLOBAL = 0 << 8
PAGE_ENTRY_ALLOC = 1 << 9
PAGE_ENTRY_CUSTOM = 0 << 10
#############

#*****************************************************************************#
# class for 4Kb Mode


class PageMode_4kb:
    total_pages = 1023
    write_page_entry_bin = "I"
    size_addressed_per_pde = (1024 * 4096)  # 4MB In Bytes

    # return the page directory number for the give address
    def get_pde_number(self, value):
        return (value >> 22) & 0x3FF

    # return the page table number for the given address
    def get_pte_number(self, value):
        return (value >> 12) & 0x3FF

    # get the total number of pd available
    def get_number_of_pd(self):
        return len(list_of_pde.keys())

    # the return value will have the page address and it is assumed
    # to be a 4096 boundary
    # hence the output of this API will be a 20bit address of the page table
    def address_of_page_table(self, page_table_number):
        global pd_start_addr

        # location from where the Page tables will be written
        PT_start_addr = pd_start_addr + 4096
        return ((PT_start_addr +
                 (page_tables_list.index(page_table_number) * 4096) >> 12))

    #   union x86_mmu_pde_pt {
    # 	u32_t  value;
    # 	struct {
    # 		u32_t p:1;
    # 		u32_t rw:1;
    # 		u32_t us:1;
    # 		u32_t pwt:1;
    # 		u32_t pcd:1;
    # 		u32_t a:1;
    # 		u32_t ignored1:1;
    # 		u32_t ps:1;
    # 		u32_t ignored2:4;
    # 		u32_t page_table:20;
    # 	};
    # };
    def get_binary_pde_value(self, value):
        perms = value.page_entries_info[0].permissions

        present = PAGE_ENTRY_PRESENT
        read_write = check_bits(perms, [1, 29]) << 1
        user_mode = check_bits(perms, [2, 28]) << 2

        pwt = PAGE_ENTRY_PWT
        pcd = PAGE_ENTRY_PCD
        a = PAGE_ENTRY_ACCESSED
        ps = 0 << 7  # this is a read only field
        page_table = self.address_of_page_table(value.pde_index) << 12
        return (present |
                read_write |
                user_mode |
                pwt |
                pcd |
                a |
                ps |
                page_table)

    # union x86_mmu_pte {
    # 	u32_t  value;
    # 	struct {
    # 		u32_t p:1;
    # 		u32_t rw:1;
    # 		u32_t us:1;
    # 		u32_t pwt:1;
    # 		u32_t pcd:1;
    # 		u32_t a:1;
    # 		u32_t d:1;
    # 		u32_t pat:1;
    # 		u32_t g:1;
    # 		u32_t alloc:1;
    # 		u32_t custom:2;
    # 		u32_t page:20;
    # 	};
    # };
    def get_binary_pte_value(self, value, pte, perm_for_pte):
        present = PAGE_ENTRY_PRESENT
        read_write = ((perm_for_pte >> 1) & 0x1) << 1
        user_mode = ((perm_for_pte >> 2) & 0x1) << 2
        pwt = PAGE_ENTRY_PWT
        pcd = PAGE_ENTRY_PCD
        a = PAGE_ENTRY_ACCESSED
        d = PAGE_ENTRY_DIRTY
        pat = PAGE_ENTRY_PAT
        g = PAGE_ENTRY_GLOBAL
        alloc = PAGE_ENTRY_ALLOC
        custom = PAGE_ENTRY_CUSTOM

        # This points to the actual memory in the HW
        # totally 20 bits to rep the phy address
        # first 10 is the number got from pde and next 10 is pte
        page_table = ((value.pde_index << 10) | pte) << 12

        binary_value = (present | read_write | user_mode |
                        pwt | pcd | a | d | pat | g | alloc | custom |
                        page_table)
        return binary_value

    def populate_required_structs(self):
        for region in raw_info:
            pde_index = self.get_pde_number(region[0])
            pte_valid_addr_start = self.get_pte_number(region[0])

            # Get the end of the page table entries
            # Since a memory region can take up only a few entries in the Page
            # table, this helps us get the last valid PTE.
            pte_valid_addr_end = self.get_pte_number(region[0] +
                                                     region[1] - 1)

            mem_size = region[1]

            # In-case the start address aligns with a page table entry other
            # than zero and the mem_size is greater than (1024*4096) i.e 4MB
            # in case where it overflows the currenty PDE's range then limit the
            # PTE to 1024 and so make the mem_size reflect the actual size taken
            # up in the current PDE
            if (region[1] + (pte_valid_addr_start * 4096)) >= \
               (self.size_addressed_per_pde):

                pte_valid_addr_end = self.total_pages
                mem_size = (((self.total_pages + 1) -
                             pte_valid_addr_start) * 4096)

            self.set_pde_pte_values(pde_index, region[0], mem_size,
                                    pte_valid_addr_start,
                                    pte_valid_addr_end,
                                    region[2])

            if pde_index not in page_tables_list:
                page_tables_list.append(pde_index)

            # IF the current pde couldn't fit the entire requested region size
            # then there is a need to create new PDEs to match the size.
            # Here the overflow_size represents the size that couldn't be fit
            # inside the current PDE, this is will now to used to create a
            # new PDE/PDEs so the size remaining will be
            # requested size - allocated size(in the current PDE)

            overflow_size = region[1] - mem_size

            # create all the extra PDEs needed to fit the requested size
            # this loop starts from the current pde till the last pde that is
            # needed the last pde is calcualted as the (start_addr + size) >>
            # 22
            if overflow_size != 0:
                for extra_pde in range(pde_index + 1, self.get_pde_number(
                        region[0] + region[1]) + 1):

                    # new pde's start address
                    # each page directory entry has a addr range of (1024 *4096)
                    # thus the new PDE start address is a multiple of that
                    # number
                    extra_pde_start_address = (extra_pde *
                                               (self.size_addressed_per_pde))

                    # the start address of and extra pde will always be 0
                    # and the end address is calculated with the new pde's start
                    # address and the overflow_size
                    extra_pte_valid_addr_end = self.get_pte_number(
                        extra_pde_start_address + overflow_size - 1)

                    # if the overflow_size couldn't be fit inside this new pde
                    # then need another pde and so we now need to limit the end
                    # of the PTE to 1024 and set the size of this new region to
                    # the max possible
                    extra_region_size = overflow_size
                    if overflow_size >= (self.size_addressed_per_pde):
                        extra_region_size = self.size_addressed_per_pde
                        extra_pte_valid_addr_end = self.total_pages

                    # load the new PDE's details

                    self.set_pde_pte_values(extra_pde,
                                            extra_pde_start_address,
                                            extra_region_size,
                                            0,
                                            extra_pte_valid_addr_end,
                                            region[2])

                    # for the next iteration of the loop the size needs to
                    # decreased.
                    overflow_size -= extra_region_size

                    # print(hex_32(overflow_size),extra_pde)
                    if extra_pde not in page_tables_list:
                        page_tables_list.append(extra_pde)

                    if overflow_size == 0:
                        break

        page_tables_list.sort()

    # update the tuple values for the memory regions needed
    def set_pde_pte_values(self, pde_index, address, mem_size,
                           pte_valid_addr_start, pte_valid_addr_end, perm):

        pages_tuple = valid_pages_inside_pde(
            start_addr=address,
            size=mem_size,
            pte_valid_addr_start=pte_valid_addr_start,
            pte_valid_addr_end=pte_valid_addr_end,
            permissions=perm)

        mem_region_values = mmu_region_details(pde_index=pde_index,
                                               page_entries_info=[])

        mem_region_values.page_entries_info.append(pages_tuple)

        if pde_index in list_of_pde.keys():
            # this step adds the new page info to the exsisting pages info
            list_of_pde[pde_index].page_entries_info.append(pages_tuple)
        else:
            list_of_pde[pde_index] = mem_region_values

    def page_directory_create_binary_file(self):
        global output_buffer
        global output_offset
        for pde in range(self.total_pages + 1):
            binary_value = 0  # the page directory entry is not valid

            # if i have a valid entry to populate
            if pde in sorted(list_of_pde.keys()):
                value = list_of_pde[pde]
                binary_value = self.get_binary_pde_value(value)
                self.pde_verbose_output(pde, binary_value)

            struct.pack_into(self.write_page_entry_bin,
                             output_buffer,
                             output_offset,
                             binary_value)

            output_offset += struct.calcsize(self.write_page_entry_bin)

    def page_table_create_binary_file(self):
        global output_buffer
        global output_offset

        for key, value in sorted(list_of_pde.items()):
            for pte in range(self.total_pages + 1):
                binary_value = 0  # the page directory entry is not valid

                valid_pte = 0
                for i in value.page_entries_info:
                    temp_value = ((pte >= i.pte_valid_addr_start) and
                                  (pte <= i.pte_valid_addr_end))
                    if temp_value:
                        perm_for_pte = i.permissions
                    valid_pte |= temp_value

                # if i have a valid entry to populate
                if valid_pte:
                    binary_value = self.get_binary_pte_value(value,
                                                             pte,
                                                             perm_for_pte)
                    self.pte_verbose_output(key, pte, binary_value)

                struct.pack_into(self.write_page_entry_bin,
                                 output_buffer,
                                 output_offset,
                                 binary_value)
                output_offset += struct.calcsize(self.write_page_entry_bin)

    # To populate the binary file the module struct needs a buffer of the
    # excat size. This returns the size needed for the given set of page
    # tables.
    def set_binary_file_size(self):
        binary_size = ctypes.create_string_buffer((4096) +
                                                  (len(list_of_pde.keys()) *
                                                   4096))
        return binary_size

    # prints the details of the pde
    def verbose_output(self):

        print("\nTotal Page directory entries " + str(self.get_number_of_pd()))
        count = 0
        for key, value in list_of_pde.items():
            for i in value.page_entries_info:
                count += 1
                print("In Page directory entry " +
                      format_string(value.pde_index) +
                      ": valid start address = " +
                      hex_32(i.start_addr) + ", end address = " +
                      hex_32((i.pte_valid_addr_end + 1) * 4096 - 1 +
                             (value.pde_index * (FourMB))))

    # print all the tables for a given page table mode
    def print_all_page_table_info(self):
        self.pde_print_elements()
        self.pte_print_elements()

    def pde_verbose_output(self, pde, binary_value):
        if args.verbose < 2:
            return

        global print_string_pde_list

        present = format_string(binary_value & 0x1)
        read_write = format_string((binary_value >> 1) & 0x1)
        user_mode = format_string((binary_value >> 2) & 0x1)
        pwt = format_string((binary_value >> 3) & 0x1)
        pcd = format_string((binary_value >> 4) & 0x1)
        a = format_string((binary_value >> 5) & 0x1)
        ignored1 = format_string(0)
        ps = format_string((binary_value >> 7) & 0x1)
        ignored2 = format_string(0000)
        page_table_addr = format_string(hex((binary_value >> 12) & 0xFFFFF))

        print_string_pde_list += (format_string(str(pde)) +
                                  " | " +
                                  (present) +
                                  " | " +
                                  (read_write) + " | " +
                                  (user_mode) + " | " +
                                  (pwt) + " | " +
                                  (pcd) + " | " +
                                  (a) + " | " +
                                  (ps) + " | " +
                                  page_table_addr + "\n"
                                  )

    def pde_print_elements(self):
        global print_string_pde_list
        print("PAGE DIRECTORY ")
        print(format_string("PDE") + " | " +
              format_string('P') + " | " +
              format_string('rw') + " | " +
              format_string('us') + " | " +
              format_string('pwt') + " | " +
              format_string('pcd') + " | " +
              format_string('a') + " | " +
              format_string('ps') + " | " +
              format_string('Addr page table'))
        print(print_string_pde_list)
        print("END OF PAGE DIRECTORY")

    def pte_verbose_output(self, pde, pte, binary_value):
        global pde_pte_string

        present = format_string((binary_value >> 0) & 0x1)
        read_write = format_string((binary_value >> 1) & 0x1)
        user_mode = format_string((binary_value >> 2) & 0x1)
        pwt = format_string((binary_value >> 3) & 0x1)
        pcd = format_string((binary_value >> 4) & 0x1)
        a = format_string((binary_value >> 5) & 0x1)
        d = format_string((binary_value >> 6) & 0x1)
        pat = format_string((binary_value >> 7) & 0x1)
        g = format_string((binary_value >> 8) & 0x1)
        alloc = format_string((binary_value >> 9) & 0x1)
        custom = format_string((binary_value >> 10) & 0x3)
        page_table_addr = hex_20((binary_value >> 12) & 0xFFFFF)

        print_string_list = (format_string(str(pte)) + " | " +
                             (present) + " | " +
                             (read_write) + " | " +
                             (user_mode) + " | " +
                             (pwt) + " | " +
                             (pcd) + " | " +
                             (a) + " | " +
                             (d) + " | " +
                             (pat) + " | " +
                             (g) + " | " +
                             (alloc) + " | " +
                             (custom) + " | " +
                             page_table_addr + "\n"
                             )

        if pde in pde_pte_string.keys():
            pde_pte_string[pde] += (print_string_list)
        else:
            pde_pte_string[pde] = print_string_list

    def pte_print_elements(self):
        global pde_pte_string

        for pde, print_string in sorted(pde_pte_string.items()):
            print("\nPAGE TABLE " + str(pde))

            print(format_string("PTE") + " | " +
                  format_string('P') + " | " +
                  format_string('rw') + " | " +
                  format_string('us') + " | " +
                  format_string('pwt') + " | " +
                  format_string('pcd') + " | " +
                  format_string('a') + " | " +
                  format_string('d') + " | " +
                  format_string('pat') + " | " +
                  format_string('g') + " | " +
                  format_string('alloc') + " | " +
                  format_string('custom') + " | " +
                  format_string('page addr'))
            print(print_string)
            print("END OF PAGE TABLE " + str(pde))


#*****************************************************************************#
# class for PAE 4KB Mode
class PageMode_PAE:
    total_pages = 511
    write_page_entry_bin = "Q"
    size_addressed_per_pde = (512 * 4096)  # 2MB In Bytes
    size_addressed_per_pdpte = (512 * size_addressed_per_pde)  # In Bytes
    list_of_pdpte = {}
    pdpte_print_string = {}
    print_string_pdpte_list = ''

    # TODO enable all page tables on just a flag

    def __init__(self):
        for i in range(4):
            self.list_of_pdpte[i] = mmu_region_details_pdpt(pdpte_index=i,
                                                            pd_entries={})

    # return the pdpte number for the give address
    def get_pdpte_number(self, value):
        return (value >> 30) & 0x3

    # return the page directory number for the give address
    def get_pde_number(self, value):
        return (value >> 21) & 0x1FF

    # return the page table number for the given address
    def get_pte_number(self, value):
        return (value >> 12) & 0x1FF

    def get_number_of_pd(self):
        return len(self.get_pdpte_list())

    def get_pdpte_list(self):
        return list({temp[0] for temp in pd_tables_list})

    # the return value will have the page address and it is assumed to be a 4096
    # boundary.hence the output of this API will be a 20bit address of the page
    # table
    def address_of_page_table(self, pdpte, page_table_number):
        global pd_start_addr

        # first page given to page directory pointer
        # and 2nd page till 5th page are used for storing the page directories.

        # set the max pdpte used. this tells how many pd are needed after
        # that we start keeping the pt
        PT_start_addr = self.get_number_of_pd() * 4096 +\
            pd_start_addr + 4096
        return (PT_start_addr +
                (pd_tables_list.index([pdpte, page_table_number]) *
                 4096) >> 12)

    #   union x86_mmu_pae_pde {
    # 	u64_t  value;
    # 	struct {
    # 		u64_t p:1;
    # 		u64_t rw:1;
    # 		u64_t us:1;
    # 		u64_t pwt:1;
    # 		u64_t pcd:1;
    # 		u64_t a:1;
    # 		u64_t ignored1:1;
    # 		u64_t ps:1;
    # 		u64_t ignored2:4;
    # 		u64_t page_table:20;
    # 		u64_t igonred3:29;
    # 		u64_t xd:1;
    # 	};
    # };

    def get_binary_pde_value(self, pdpte, value):
        perms = value.page_entries_info[0].permissions

        present = PAGE_ENTRY_PRESENT
        read_write = check_bits(perms, [1, 29]) << 1
        user_mode = check_bits(perms, [2, 28]) << 2

        pwt = PAGE_ENTRY_PWT
        pcd = PAGE_ENTRY_PCD
        a = PAGE_ENTRY_ACCESSED
        ps = 0 << 7  # set to make sure that the phy page is 4KB
        page_table = self.address_of_page_table(pdpte, value.pde_index) << 12
        xd = 0
        return (present |
                read_write |
                user_mode |
                pwt |
                pcd |
                a |
                ps |
                page_table |
                xd)

    # union x86_mmu_pae_pte {
    # 	u64_t  value;
    # 	struct {
    # 		u64_t p:1;
    # 		u64_t rw:1;
    # 		u64_t us:1;
    # 		u64_t pwt:1;
    # 		u64_t pcd:1;
    # 		u64_t a:1;
    # 		u64_t d:1;
    # 		u64_t pat:1;
    # 		u64_t g:1;
    # 		u64_t ignore:3;
    # 		u64_t page:20;
    # 		u64_t igonred3:29;
    # 		u64_t xd:1;
    # 	};
    # };
    def get_binary_pte_value(self, value, pde, pte, perm_for_pte):
        present = PAGE_ENTRY_PRESENT
        read_write = perm_for_pte & PAGE_ENTRY_READ_WRITE
        user_mode = perm_for_pte & PAGE_ENTRY_USER_SUPERVISOR
        pwt = PAGE_ENTRY_PWT
        pcd = PAGE_ENTRY_PCD
        a = PAGE_ENTRY_ALLOC
        d = PAGE_ENTRY_DIRTY
        pat = PAGE_ENTRY_PAT
        g = PAGE_ENTRY_GLOBAL

        # This points to the actual memory in the HW
        # totally 20 bits to rep the phy address
        # first 2bits is from pdpte then 9bits is the number got from pde and
        # next 9bits is pte
        page_table = ((value.pdpte_index << 18) | (pde << 9) | pte) << 12

        xd = ((perm_for_pte >> 63) & 0x1) << 63

        binary_value = (present | read_write | user_mode |
                        pwt | pcd | a | d | pat | g |
                        page_table | xd)
        return binary_value

    def clean_up_unused_pdpte(self):
        self.list_of_pdpte = {key: value for key, value in
                              self.list_of_pdpte.items()
                              if value.pd_entries != {}}

    # update the tuple values for the memory regions needed
    def set_pde_pte_values(self, pdpte, pde_index, address, mem_size,
                           pte_valid_addr_start, pte_valid_addr_end, perm):

        pages_tuple = valid_pages_inside_pde(
            start_addr=address,
            size=mem_size,
            pte_valid_addr_start=pte_valid_addr_start,
            pte_valid_addr_end=pte_valid_addr_end,
            permissions=perm)

        mem_region_values = mmu_region_details(pde_index=pde_index,
                                               page_entries_info=[])

        mem_region_values.page_entries_info.append(pages_tuple)

        if pde_index in self.list_of_pdpte[pdpte].pd_entries.keys():
            # this step adds the new page info to the exsisting pages info
            self.list_of_pdpte[pdpte].pd_entries[pde_index].\
                page_entries_info.append(pages_tuple)
        else:
            self.list_of_pdpte[pdpte].pd_entries[pde_index] = mem_region_values

    def populate_required_structs(self):
        for region in raw_info:
            pdpte_index = self.get_pdpte_number(region[0])
            pde_index = self.get_pde_number(region[0])
            pte_valid_addr_start = self.get_pte_number(region[0])

            # Get the end of the page table entries
            # Since a memory region can take up only a few entries in the Page
            # table, this helps us get the last valid PTE.
            pte_valid_addr_end = self.get_pte_number(region[0] +
                                                     region[1] - 1)

            mem_size = region[1]

            # In-case the start address aligns with a page table entry other
            # than zero and the mem_size is greater than (1024*4096) i.e 4MB
            # in case where it overflows the currenty PDE's range then limit the
            # PTE to 1024 and so make the mem_size reflect the actual size
            # taken up in the current PDE
            if (region[1] + (pte_valid_addr_start * 4096)) >= \
               (self.size_addressed_per_pde):

                pte_valid_addr_end = self.total_pages
                mem_size = (((self.total_pages + 1) -
                             pte_valid_addr_start) * 4096)

            self.set_pde_pte_values(pdpte_index,
                                    pde_index,
                                    region[0],
                                    mem_size,
                                    pte_valid_addr_start,
                                    pte_valid_addr_end,
                                    region[2])

            if [pdpte_index, pde_index] not in pd_tables_list:
                pd_tables_list.append([pdpte_index, pde_index])

            # IF the current pde couldn't fit the entire requested region
            # size then there is a need to create new PDEs to match the size.
            # Here the overflow_size represents the size that couldn't be fit
            # inside the current PDE, this is will now to used to create a new
            # PDE/PDEs so the size remaining will be
            # requested size - allocated size(in the current PDE)

            overflow_size = region[1] - mem_size

            # create all the extra PDEs needed to fit the requested size
            # this loop starts from the current pde till the last pde that is
            # needed the last pde is calcualted as the (start_addr + size) >>
            # 22
            if overflow_size != 0:
                for extra_pdpte in range(pdpte_index,
                                         self.get_pdpte_number(region[0] +
                                                               region[1]) + 1):
                    for extra_pde in range(pde_index + 1, self.get_pde_number(
                            region[0] + region[1]) + 1):

                        # new pde's start address
                        # each page directory entry has a addr range of
                        # (1024 *4096) thus the new PDE start address is a
                        # multiple of that number
                        extra_pde_start_address = (
                            extra_pde * (self.size_addressed_per_pde))

                        # the start address of and extra pde will always be 0
                        # and the end address is calculated with the new
                        # pde's start address and the overflow_size
                        extra_pte_valid_addr_end = (
                            self.get_pte_number(extra_pde_start_address +
                                                overflow_size - 1))

                        # if the overflow_size couldn't be fit inside this new
                        # pde then need another pde and so we now need to limit
                        # the end of the PTE to 1024 and set the size of this
                        # new region to the max possible
                        extra_region_size = overflow_size
                        if overflow_size >= (self.size_addressed_per_pde):
                            extra_region_size = self.size_addressed_per_pde
                            extra_pte_valid_addr_end = self.total_pages

                        # load the new PDE's details

                        self.set_pde_pte_values(extra_pdpte,
                                                extra_pde,
                                                extra_pde_start_address,
                                                extra_region_size,
                                                0,
                                                extra_pte_valid_addr_end,
                                                region[2])

                        # for the next iteration of the loop the size needs
                        # to decreased
                        overflow_size -= extra_region_size

                        if [extra_pdpte, extra_pde] not in pd_tables_list:
                            pd_tables_list.append([extra_pdpte, extra_pde])

                        if overflow_size == 0:
                            break

        pd_tables_list.sort()
        self.clean_up_unused_pdpte()

    def pdpte_create_binary_file(self):
        global output_buffer
        global output_offset
        global pd_start_addr

        # pae needs a pdpte at 32byte aligned address

        # Even though we have only 4 entries in the pdpte we need to move
        # the output_offset variable to the next page to start pushing
        # the pd contents
        for pdpte in range(self.total_pages + 1):
            if pdpte in self.get_pdpte_list():
                present = 1 << 0
                pwt = 0 << 3
                pcd = 0 << 4
                addr_of_pd = (((pd_start_addr + 4096) +
                               self.get_pdpte_list().index(pdpte) *
                               4096) >> 12) << 12
                binary_value = (present | pwt | pcd | addr_of_pd)
                self.pdpte_verbose_output(pdpte, binary_value)
            else:
                binary_value = 0

            struct.pack_into(self.write_page_entry_bin,
                             output_buffer,
                             output_offset,
                             binary_value)

            output_offset += struct.calcsize(self.write_page_entry_bin)

    def page_directory_create_binary_file(self):
        global output_buffer
        global output_offset
        pdpte_number_count = 0
        for pdpte, pde_info in self.list_of_pdpte.items():

            pde_number_count = 0
            for pde in range(self.total_pages + 1):
                binary_value = 0  # the page directory entry is not valid

                # if i have a valid entry to populate
                # if pde in sorted(list_of_pde.keys()):
                if pde in sorted(pde_info.pd_entries.keys()):
                    value = pde_info.pd_entries[pde]
                    binary_value = self.get_binary_pde_value(pdpte, value)
                    self.pde_verbose_output(pdpte, pde, binary_value)

                pde_number_count += 1
                struct.pack_into(self.write_page_entry_bin,
                                 output_buffer,
                                 output_offset,
                                 binary_value)

                output_offset += struct.calcsize(self.write_page_entry_bin)

    def page_table_create_binary_file(self):
        global output_buffer
        global output_offset

        pdpte_number_count = 0
        for pdpte, pde_info in sorted(self.list_of_pdpte.items()):
            pdpte_number_count += 1
            for pde, pte_info in sorted(pde_info.pd_entries.items()):
                pte_number_count = 0
                for pte in range(self.total_pages + 1):
                    binary_value = 0  # the page directory entry is not valid

                    valid_pte = 0
                    # go through all the valid pages inside the pde to
                    # figure out if we need to populate this pte
                    for i in pte_info.page_entries_info:
                        temp_value = ((pte >= i.pte_valid_addr_start) and
                                      (pte <= i.pte_valid_addr_end))
                        if temp_value:
                            perm_for_pte = i.permissions
                        valid_pte |= temp_value

                    # if i have a valid entry to populate
                    if valid_pte:
                        binary_value = self.get_binary_pte_value(pde_info,
                                                                 pde,
                                                                 pte,
                                                                 perm_for_pte)
                        pte_number_count += 1
                        self.pte_verbose_output(pdpte, pde, pte, binary_value)

                    # print(binary_value, (self.write_page_entry_bin))

                    struct.pack_into(self.write_page_entry_bin,
                                     output_buffer,
                                     output_offset,
                                     binary_value)
                    output_offset += struct.calcsize(self.write_page_entry_bin)

    # To populate the binary file the module struct needs a buffer of the
    # excat size This returns the size needed for the given set of page tables.
    def set_binary_file_size(self):
        pages_for_pdpte = 1
        pages_for_pd = self.get_number_of_pd()
        pages_for_pt = len(pd_tables_list)
        binary_size = ctypes.create_string_buffer((pages_for_pdpte +
                                                   pages_for_pd +
                                                   pages_for_pt) * 4096)
        return binary_size

    # prints the details of the pde
    def verbose_output(self):
        print("\nTotal Page directory Page pointer entries " +
              str(self.get_number_of_pd()))
        count = 0
        for pdpte, pde_info in sorted(self.list_of_pdpte.items()):
            print(
                "In page directory page table pointer " +
                format_string(pdpte))

            for pde, pte_info in sorted(pde_info.pd_entries.items()):
                for pte in pte_info.page_entries_info:
                    count += 1
                    print("    In Page directory entry " + format_string(pde) +
                          ": valid start address = " +
                          hex_32(pte.start_addr) + ", end address = " +
                          hex_32((pte.pte_valid_addr_end + 1) * 4096 - 1 +
                                 (pde * (self.size_addressed_per_pde)) +
                                 (pdpte * self.size_addressed_per_pdpte)))

    def pdpte_verbose_output(self, pdpte, binary_value):
        if args.verbose < 2:
            return

        present = format_string(binary_value & 0x1)
        pwt = format_string((binary_value >> 3) & 0x1)
        pcd = format_string((binary_value >> 4) & 0x1)
        page_table_addr = format_string(hex((binary_value >> 12) & 0xFFFFF))

        self.print_string_pdpte_list += (format_string(str(pdpte)) +
                                         " | " + (present) + " | " +
                                         (pwt) + " | " +
                                         (pcd) + " | " +
                                         page_table_addr + "\n")

    def pdpte_print_elements(self):
        print("\nPAGE DIRECTORIES POINTER ")
        print(format_string("PDPTE") + " | " +
              format_string('P') + " | " +
              format_string('pwt') + " | " +
              format_string('pcd') + " | " +
              format_string('Addr'))
        print(self.print_string_pdpte_list)
        print("END OF PAGE DIRECTORY POINTER")

    def pde_verbose_output(self, pdpte, pde, binary_value):
        if args.verbose < 2:
            return

        global print_string_pde_list

        present = format_string(binary_value & 0x1)
        read_write = format_string((binary_value >> 1) & 0x1)
        user_mode = format_string((binary_value >> 2) & 0x1)
        pwt = format_string((binary_value >> 3) & 0x1)
        pcd = format_string((binary_value >> 4) & 0x1)
        a = format_string((binary_value >> 5) & 0x1)
        ignored1 = format_string(0)
        ps = format_string((binary_value >> 7) & 0x1)
        ignored2 = format_string(0000)
        page_table_addr = format_string(hex((binary_value >> 12) & 0xFFFFF))
        xd = format_string((binary_value >> 63) & 0x1)

        print_string_pde_list = (format_string(str(pde)) + " | " +
                                 (present) + " | " +
                                 (read_write) + " | " +
                                 (user_mode) + " | " +
                                 (pwt) + " | " +
                                 (pcd) + " | " +
                                 (a) + " | " +
                                 (ps) + " | " +
                                 page_table_addr + " | " +
                                 (xd) + "\n")

        if pdpte in self.pdpte_print_string.keys():
            self.pdpte_print_string[pdpte] += (print_string_pde_list)
        else:
            self.pdpte_print_string[pdpte] = print_string_pde_list

    # print all the tables for a given page table mode
    def print_all_page_table_info(self):
        self.pdpte_print_elements()
        self.pde_print_elements()
        self.pte_print_elements()

    def pde_print_elements(self):
        global print_string_pde_list

        for pdpte, print_string in sorted(self.pdpte_print_string.items()):
            print("\n PAGE DIRECTORIES for PDPT " + str(pdpte))
            print(format_string("PDE") + " | " +
                  format_string('P') + " | " +
                  format_string('rw') + " | " +
                  format_string('us') + " | " +
                  format_string('pwt') + " | " +
                  format_string('pcd') + " | " +
                  format_string('a') + " | " +
                  format_string('ps') + " | " +
                  format_string('Addr') + " | " +
                  format_string('xd'))
            print(print_string)
            print("END OF PAGE DIRECTORIES for PDPT " + str(pdpte))

    def pte_verbose_output(self, pdpte, pde, pte, binary_value):
        global pde_pte_string

        present = format_string((binary_value >> 0) & 0x1)
        read_write = format_string((binary_value >> 1) & 0x1)
        user_mode = format_string((binary_value >> 2) & 0x1)
        pwt = format_string((binary_value >> 3) & 0x1)
        pcd = format_string((binary_value >> 4) & 0x1)
        a = format_string((binary_value >> 5) & 0x1)
        d = format_string((binary_value >> 6) & 0x1)
        pat = format_string((binary_value >> 7) & 0x1)
        g = format_string((binary_value >> 8) & 0x1)
        page_table_addr = hex_20((binary_value >> 12) & 0xFFFFF)
        xd = format_string((binary_value >> 63) & 0x1)

        print_string_list = (format_string(str(pte)) + " | " +
                             (present) + " | " +
                             (read_write) + " | " +
                             (user_mode) + " | " +
                             (pwt) + " | " +
                             (pcd) + " | " +
                             (a) + " | " +
                             (d) + " | " +
                             (pat) + " | " +
                             (g) + " | " +
                             page_table_addr + " | " +
                             (xd) + "\n"
                             )

        if (pdpte, pde) in pde_pte_string.keys():
            pde_pte_string[(pdpte, pde)] += (print_string_list)
        else:
            pde_pte_string[(pdpte, pde)] = print_string_list

    def pte_print_elements(self):
        global pde_pte_string

        for (pdpte, pde), print_string in sorted(pde_pte_string.items()):
            print(
                "\nPAGE TABLE for PDPTE = " +
                str(pdpte) +
                " and PDE = " +
                str(pde))

            print(format_string("PTE") + " | " +
                  format_string('P') + " | " +
                  format_string('rw') + " | " +
                  format_string('us') + " | " +
                  format_string('pwt') + " | " +
                  format_string('pcd') + " | " +
                  format_string('a') + " | " +
                  format_string('d') + " | " +
                  format_string('pat') + " | " +
                  format_string('g') + " | " +
                  format_string('Page Addr') + " | " +
                  format_string('xd'))
            print(print_string)
            print("END OF PAGE TABLE " + str(pde))


#*****************************************************************************#


def print_list_of_pde(list_of_pde):
    for key, value in list_of_pde.items():
        print(key, value)
        print('\n')


# read the binary from the input file and populate a dict for
# start address of mem region
# size of the region - so page tables entries will be created with this
# read write permissions

def read_mmu_list_marshal_param(page_mode):

    global read_buff
    global page_tables_list
    global pd_start_addr
    global validation_issue_memory_overlap
    read_buff = input_file.read()
    input_file.close()

    # read contents of the binary file first 2 values read are
    # num_of_regions and page directory start address both calculated and
    # populated by the linker
    num_of_regions, pd_start_addr = struct.unpack_from(
        header_values_format, read_buff, 0)

    # a offset used to remember next location to read in the binary
    size_read_from_binary = struct.calcsize(header_values_format)

    # for each of the regions mentioned in the binary loop and populate all the
    # required parameters
    for region in range(num_of_regions):
        basic_mem_region_values = struct.unpack_from(struct_mmu_regions_format,
                                                     read_buff,
                                                     size_read_from_binary)
        size_read_from_binary += struct.calcsize(struct_mmu_regions_format)

        # ignore zero sized memory regions
        if basic_mem_region_values[1] == 0:
            continue

        # validate for memory overlap here
        for i in raw_info:
            start_location = basic_mem_region_values[0]
            end_location = basic_mem_region_values[0] + \
                basic_mem_region_values[1]

            overlap_occurred = ((start_location >= i[0]) and
                                (start_location <= (i[0] + i[1]))) and \
                ((end_location >= i[0]) and
                 (end_location <= i[0] + i[1]))

            if overlap_occurred:
                validation_issue_memory_overlap = [
                    True,
                    start_location,
                    page_mode.get_pde_number(start_location)]
                return

        # add the retrived info another list
        raw_info.append(basic_mem_region_values)


def validate_pde_regions():
    # validation for correct page alignment of the regions
    for key, value in list_of_pde.items():
        for pages_inside_pde in value.page_entries_info:
            if pages_inside_pde.start_addr & (0xFFF) != 0:
                print("Memory Regions are not page aligned",
                      hex(pages_inside_pde.start_addr))
                sys.exit(2)

            # validation for correct page alignment of the regions
            if pages_inside_pde.size & (0xFFF) != 0:
                print("Memory Regions size is not page aligned",
                      hex(pages_inside_pde.size))
                sys.exit(2)

    # validation for spiling of the regions across various
    if validation_issue_memory_overlap[0] == True:
        print("Memory Regions are overlapping at memory address " +
              str(hex(validation_issue_memory_overlap[1])) +
              " with Page directory Entry number " +
              str(validation_issue_memory_overlap[2]))
        sys.exit(2)


def check_bits(val, bits):
    for b in bits:
        if val & (1 << b):
            return 1
    return 0


# Read the parameters passed to the file
def parse_args():
    global args

    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)

    parser.add_argument("-e", "--big-endian", action="store_true",
                        help="Target encodes data in big-endian format"
                        "(little endian is the default)")

    parser.add_argument("-i", "--input",
                        help="Input file from which MMU regions are read.")
    parser.add_argument("-k", "--kernel",
                        help="Zephyr kernel image")
    parser.add_argument(
        "-o", "--output",
        help="Output file into which the page tables are written.")
    parser.add_argument("-v", "--verbose", action="count", default=0,
                        help="Print debugging information. Multiple "
                             "invocations increase verbosity")
    args = parser.parse_args()
    if "VERBOSE" in os.environ:
        args.verbose = 1


# the format for writing in the binary file would be decided by the
# endian selected
def set_struct_endian_format(page_mode):
    endian_string = "<"
    if args.big_endian is True:
        endian_string = ">"
    global struct_mmu_regions_format
    global header_values_format

    struct_mmu_regions_format = endian_string + "IIQ"
    header_values_format = endian_string + "II"
    page_mode.write_page_entry_bin = (endian_string +
                                      page_mode.write_page_entry_bin)


def format_string(input_str):
    output_str = '{0: <5}'.format(str(input_str))
    return output_str

# format for 32bit hex value


def hex_32(input_value):
    output_value = "{0:#0{1}x}".format(input_value, 10)
    return output_value

# format for 20bit hex value


def hex_20(input_value):
    output_value = "{0:#0{1}x}".format(input_value, 7)
    return output_value


def verbose_output(page_mode):
    if args.verbose == 0:
        return

    print("\nMemory Regions as defined:")
    for info in raw_info:
        print("Memory region start address = " + hex_32(info[0]) +
              ", Memory size = " + hex_32(info[1]) +
              ", Permission = " + hex(info[2]))

    page_mode.verbose_output()

    if args.verbose > 1:
        page_mode.print_all_page_table_info()

# build sym table


def get_symbols(obj):
    for section in obj.iter_sections():
        if isinstance(section, SymbolTableSection):
            return {sym.name: sym.entry.st_value
                    for sym in section.iter_symbols()}

    raise LookupError("Could not find symbol table")


# determine which paging mode was selected
def get_page_mode():
    with open(args.kernel, "rb") as fp:
        kernel = ELFFile(fp)
        sym = get_symbols(kernel)
    try:
        return sym["CONFIG_X86_PAE_MODE"]
    except BaseException:
        return 0


def main():
    global output_buffer
    parse_args()

    # select the page table needed
    if get_page_mode():
        page_mode = PageMode_PAE()
    else:
        page_mode = PageMode_4kb()

    set_struct_endian_format(page_mode)

    global input_file
    input_file = open(args.input, 'rb')

    global binary_output_file
    binary_output_file = open(args.output, 'wb')

    # inputfile= file_name
    read_mmu_list_marshal_param(page_mode)

    # populate the required structs
    page_mode.populate_required_structs()

    # validate the inputs
    validate_pde_regions()

    # The size of the output buffer has to match the number of bytes we write
    # this corresponds to the number of page tables gets created.
    output_buffer = page_mode.set_binary_file_size()

    try:
        page_mode.pdpte_create_binary_file()
    except BaseException:
        pass
    page_mode.page_directory_create_binary_file()
    page_mode.page_table_create_binary_file()

    # write the binary data into the file
    binary_output_file.write(output_buffer)
    binary_output_file.close()

    # verbose output needed by the build system
    verbose_output(page_mode)


if __name__ == "__main__":
    main()