1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% Copyright (c) 2018, ETH Zurich. 3% All rights reserved. 4% 5% This file is distributed under the terms in the attached LICENSE file. 6% If you do not find this file, copies can be found by writing to: 7% ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. 8% Attn: Systems Group. 9%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 11% Some Conventions: 12% NodeId = identifier list 13% IAddr = [1,2,3] 14% Addr = [kind, [1,2,3]] 15% IBlock [block{..}, block{...}] 16% Block = [kind, [block{..}, block{..}]] 17 18:- module(decoding_net2). 19 20:- use_module(allocator). 21 22 23%% node_accept(InNodeId, InAddr :: block). 24:- dynamic node_accept/2. 25 26%% node_translate_dyn(InNodeId, InAddr :: block, OutNodeId, OutAddr :: block). 27:- dynamic node_translate_dyn/4. 28 29%% node_overlay(InNodeId, OutNodeId). 30:- dynamic node_overlay/2. 31 32%% This fact keeps track of blocks that are in use. 33%% node_in_use(NodeId, Block). 34:- dynamic node_in_use/2. 35 36%:- export node_accept/2. 37%:- export node_overlay/2. 38%:- export node_in_use/2. 39:- export struct(block(base,limit,props)). 40:- export struct(region(node_id,blocks)). 41:- export struct(name(node_id,address)). 42 43:- lib(ic). 44 45%% True, if any possible matching between two node ids A and B may exist. 46%node_reachable(A,A). 47%node_reachable(A,B) :- 48% node_translate_dyn(A,_,Mid,_), 49% node_reachable(Mid, B). 50% 51%node_reachable(A,B) :- 52% node_block_meta(A,_,Mid), 53% node_reachable(Mid, B). 54 55iaddress_aligned([], _). 56iaddress_aligned([A | As], Bits) :- 57 BlockSize is 2^Bits, 58 BlockNum #>= 0, 59 A #= BlockNum * BlockSize, 60 iaddress_aligned(As, Bits). 61 62address_aligned([_, IAddress], Bits) :- 63 iaddress_aligned(IAddress, Bits). 64 65name_aligned(Name, Bits) :- 66 name{address: Addr} = Name, 67 address_aligned(Addr, Bits). 68 69test_alignment :- 70 iaddress_gt([536870912], IAddr), 71 iaddress_aligned(IAddr, 21), 72 labeling(IAddr), 73 writeln(IAddr). 74 75test_alignment2 :- 76 init, add_pci, add_process, 77 Proc = region{node_id: ["OUT", "PROC0", "PROC0"]}, 78 free_region_aligned(Proc, [memory, [1024]]), 79 writeln(Proc). 80 81iblock_match(A, block{base: B, limit: L}) :- 82 B #=< A, 83 A #=< L. 84 85iblock_nomatch(A, block{base: B, limit: L}) :- 86 A #< B ; 87 A #> L. 88 89iblocks_match_any(A, [B | Bs]) :- 90 iblock_match(A, B) ; iblocks_match_any(A, Bs). 91 92iblocks_match_any_ic(A, B) :- 93 iblocks_match_any(A,B), 94 labeling([A]). 95 96% Union of blocks. [block{base:0,limit:5},block{base:33,limit:35}] -> 0,1,..,5,33,..,35 97iblock_values(Blocks, Values) :- 98 findall(X, iblocks_match_any_ic(X, Blocks), Values). 99 100 101iblocks_match([], []). 102iblocks_match([A|As], [B|Bs]) :- 103 iblock_match(A,B), 104 iblocks_match(As, Bs). 105 106iblocks_nomatch([], []). 107iblocks_nomatch([A|As], [B|Bs]) :- 108 iblock_nomatch(A,B), 109 iblocks_nomatch(As, Bs). 110 111 112% For a ic constrained variable 113iblocks_match_ic(X,Bs) :- 114 length(Bs,LiLe), 115 length(X,LiLe), 116 iblocks_match(X, Bs), 117 labeling(X). 118 119% Cross product of blocks 120iblock_crossp(Blocks, Values) :- 121 findall(X, iblocks_match_ic(X, Blocks), Values). 122 123 124address_match([K, IAddr], [K, IBlocks]) :- 125 iblocks_match(IAddr, IBlocks). 126 127iaddress_gt([], []). 128iaddress_gt([S | Ss], [B | Bs]) :- 129 S #< B, 130 iaddress_gt(Ss,Bs). 131 132% Will only compare addresses of the same kind 133address_gt([K, ISmaller], [K, IBigger]) :- 134 iaddress_gt(ISmaller, IBigger). 135 136iaddress_gte([], []). 137iaddress_gte([S | Ss], [B | Bs]) :- 138 S #=< B, 139 iaddress_gte(Ss,Bs). 140 141% Will only compare addresses of the same kind 142address_gte([K, ISmaller], [K, IBigger]) :- 143 iaddress_gte(ISmaller, IBigger). 144 145iaddress_sub([], [], []). 146iaddress_sub([A | As], [B | Bs], [C | Cs]) :- 147 C is A - B, 148 iaddress_sub(As,Bs,Cs). 149 150% A - B = C ---> address_sub(A,B,C) 151address_sub([K, IA], [K, IB], [K, IC]) :- 152 iaddress_sub(IA, IB, IC). 153 154iaddress_add([], [], []). 155iaddress_add([A | As], [B | Bs], [C | Cs]) :- 156 C is A + B, 157 iaddress_add(As,Bs,Cs). 158 159% A + B = C ---> address_add(A,B,C) 160address_add([K, IA], [K, IB], [K, IC]) :- 161 iaddress_add(IA, IB, IC). 162 163iaddress_add_const_ic([], _, []). 164iaddress_add_const_ic([A | As], B, [C | Cs]) :- 165 C #= A + B, 166 iaddress_add_const_ic(As,B,Cs). 167 168% A + B = C ---> address_add(A,B,C) 169address_add_const_ic([K, IA], B, [K, IC]) :- 170 iaddress_add_const_ic(IA, B, IC). 171 172iaddress_add_const([], _, []). 173iaddress_add_const([A | As], B, [C | Cs]) :- 174 C is A + B, 175 iaddress_add_const(As,B,Cs). 176 177% A + B = C ---> address_add(A,B,C) 178address_add_const([K, IA], B, [K, IC]) :- 179 iaddress_add_const(IA, B, IC). 180 181iaddress_var([A | As]) :- 182 var(A) ; iaddress_var(As). 183 184address_var([K, IA]) :- 185 var(K) ; iaddress_var(IA). 186 187iblock_iaddress_gt([], []). 188iblock_iaddress_gt([Block | Bs], [Addr | As]) :- 189 block{ 190 limit: Limit 191 } = Block, 192 Addr #> Limit, 193 iblock_iaddress_gt(Bs, As). 194 195block_address_gt([K, IBlocks], [K, IAddress]) :- 196 iblock_iaddress_gt(IBlocks, IAddress). 197 198 199iblock_limit_iaddress([], []). 200iblock_limit_iaddress([Block | Bs], [Addr | As]) :- 201 block{ 202 limit: Addr 203 } = Block, 204 iblock_limit_iaddress(Bs, As). 205 206% Turn the limit of the blocks into an address 207block_limit_address([K, IBlocks], [K, IAddress]) :- 208 iblock_limit_iaddress(IBlocks, IAddress). 209 210iblock_base_iaddress([], []). 211iblock_base_iaddress([Block | Bs], [Addr | As]) :- 212 block{ 213 base: Addr 214 } = Block, 215 iblock_base_iaddress(Bs, As). 216 217% Turn the base of the blocks into an address 218block_base_address([K, IBlocks], [K, IAddress]) :- 219 (var(IBlocks),var(IAddress), fail) ; 220 iblock_base_iaddress(IBlocks, IAddress). 221 222% Turn a region into a base name 223region_base_name(Region, Name) :- 224 Region = region{node_id: NodeId, blocks: Blocks}, 225 Name = name{node_id:NodeId, address: Base}, 226 block_base_address(Blocks, Base). 227 228% Turn a region into a limit name 229region_limit_name(Region, Name) :- 230 Region = region{node_id: NodeId, blocks: Blocks}, 231 block_limit_address(Blocks, Base), 232 Name = name{node_id:NodeId, address: Base}. 233 234iblock_isize([],[]). 235iblock_isize([A | As],[B | Bs]) :- 236 block{ 237 base: Base, 238 limit: Limit 239 } = A, 240 ( 241 (var(B), B is Limit - Base) ; 242 (var(Limit), Limit is Base + B) 243 ), 244 iblock_isize(As, Bs). 245 246block_size([K, IBlocks], [K, ISize]) :- 247 iblock_isize(IBlocks, ISize). 248 249region_size(Region, Size) :- 250 region{ blocks: Blocks } = Region, 251 block_size(Blocks, Size). 252 253:- export accept/2. 254accept(NodeId, Addr) :- 255 node_accept(NodeId, Block), 256 address_match(Addr, Block). 257 258iaddr_iblock_map([], [], [], []). 259iaddr_iblock_map([SrcAddr | A], [SrcBlock | B], [DstAddr | C], [DstBlock | D]) :- 260 SrcBlock = block{base:SrcBase}, 261 DstBlock = block{base:DstBase}, 262 DstAddr #= SrcAddr - SrcBase + DstBase, 263 iaddr_iblock_map(A,B,C,D). 264 265test_iaddr_iblock_map :- 266 iaddr_iblock_map([1],[block{base:0, limit:1024}], Dst, [block{base:100,limit:2000}]), 267 Dst = [101]. 268 269%% Convert from region (encoded as block) to names 270to_name(Region,Name) :- 271 region{ 272 node_id:Id, 273 blocks: Blocks % Blocks = [Kind, [block{...},block{...}]] 274 } = Region, 275 address_match(Addr, Blocks), 276 Name = name{ 277 node_id:Id, 278 address:Addr 279 }. 280 281 282default_iaddr_constraint(Addr) :- 283 Addr #>= 0, 284 Addr #< 2147483648. 285 286%% Thes functions turn an IC constrained Addr to base/limit blocks 287iaddr_to_iblock_one(Addr, Block) :- 288 default_iaddr_constraint(Addr), 289 get_bounds(Addr,Min,Max), 290 Size is Max - Min + 1, 291 ( get_domain_size(Addr,Size) -> 292 Block = block{ 293 base:Min, 294 limit:Max 295 } 296 ; 297 writeln(stderr,"Name conversion to region failed: Non continuous domain for address"), 298 fail 299 ). 300 301iaddr_to_iblocks([], []). 302iaddr_to_iblocks([A | As], [B | Bs]) :- 303 iaddr_to_iblock_one(A,B), 304 iaddr_to_iblocks(As, Bs). 305 306addr_to_blocks([K, IAddr], [K, IBlocks]) :- 307 iaddr_to_iblocks(IAddr, IBlocks). 308 309%% Convert from names to regions 310 311to_region(Name,Region) :- 312 name{ 313 node_id:Id, 314 address:Addr 315 } = Name, 316 region{ 317 node_id: Id, 318 blocks: Blocks 319 } = Region, 320 addr_to_blocks(Addr, Blocks). 321 322 323 324block_translate([SrcK, ISrcAddr], [SrcK, SrcBlock], [DstK, IDstAddr], [DstK, DstBlock]) :- 325 iaddr_iblock_map(ISrcAddr, SrcBlock, IDstAddr, DstBlock). 326 327test_block_translate :- 328 block_translate( 329 [memory,[1]], 330 [memory, [block{base:0, limit:1024}]], 331 Dst, 332 [memory, [block{base:100,limit:2000}]]), 333 Dst = [memory, [101]]. 334 335 336does_not_translate(NodeId, [AKind,IAddr]) :- 337 %TODO take node_translate_block into account 338 findall(B, node_translate_dyn(NodeId, B, _, _), Blocks), 339 (foreach([AKind, IBlock], Blocks),param(IAddr),param(AKind) do 340 iblocks_nomatch(IAddr, IBlock) 341 ). 342 343test_does_not_translate :- 344 assert(node_translate_dyn( 345 ["In"], [memory, [block{base:1000,limit:2000}]], 346 ["Out1"], [memory, [block{base:0,limit:1000}]])), 347 does_not_translate(["In"], [memory, [500]]). 348 349 350% Takes translate and overlays predicates into account. 351%:- export translate/4. 352translate(SrcNodeId, SrcAddr, DstNodeId, DstAddr) :- 353 % The conditions check if, independently of Dst, a matching translate block 354 % exists. Only if it doesnt, we consider the overlay 355 ( 356 node_translate_dyn(SrcNodeId, SrcBlock, CandidId, CandidBlock), 357 address_match(SrcAddr, SrcBlock), 358 DstNodeId = CandidId, 359 block_translate(SrcAddr, SrcBlock, DstAddr, CandidBlock) 360 ; 361 node_translate_block(SrcNodeId, SrcAddr, DstNodeId, DstAddr) 362 ; 363 does_not_translate(SrcNodeId, SrcAddr), 364 SrcAddr = DstAddr, 365 node_overlay(SrcNodeId, DstNodeId) 366 ). 367 368test_translate :- 369 add_pci, 370 translate(["IN", "IOMMU0", "PCI0"], [memory, [1]], OutNodeId, OutAddr), 371 OutNodeId = ["OUT", "IOMMU0", "PCI0"], 372 OutAddr = [memory, [1]]. 373 374test_translate2 :- 375 %Setup 376 assert(node_translate_dyn( 377 ["In"], [memory, [block{base:1000,limit:2000}]], 378 ["Out1"], [memory, [block{base:0,limit:1000}]])), 379 assert(node_overlay(["In"], ["Out2"])), 380 % Test the translate block 381 translate(["In"], [memory, [1000]], ["Out1"], [memory, [0]]), 382 % Test the overlay 383 translate(["In"], [memory, [0]], ["Out2"], [memory, [0]]), 384 % make sure the upper limit is respected. 385 translate(["In"], [memory, [2500]], ["Out2"], [memory, [2500]]), 386 % make sure no within block translation to overlay exists 387 not(translate(["In"], [memory, [1000]], ["Out2"], [memory, [1000]])). 388 389 390accept_dummy(NodeId, AcS) :- 391 node_accept(NodeId, AcS). 392 393:- export accept/1. 394accept(Name) :- 395 name{ 396 node_id:NodeId, 397 address:Addr 398 } = Name, 399 accept(NodeId, Addr). 400 401accept(Region) :- 402 region_base_name(Region,Base), 403 accept(Base), 404 region_limit_name(Region,Limit), 405 accept(Limit). 406 407 408:- export translate/2. 409translate(SrcName,DstName) :- 410 name{ 411 node_id:SrcNodeId, 412 address:SrcAddr 413 } = SrcName, 414 name{ 415 node_id:DstNodeId, 416 address:DstAddr 417 } = DstName, 418 translate(SrcNodeId,SrcAddr,DstNodeId, DstAddr). 419 420test_translate_name :- 421 add_pci, 422 translate( 423 name{node_id:["IN", "IOMMU0", "PCI0"], address: [memory, [1]]}, 424 Out), 425 Out = name{node_id: ["OUT", "IOMMU0", "PCI0"], address: [memory, [1]]}. 426 427% Reflexive, transitive closure of translate 428:- export decodes_to/2. 429decodes_to(S,S). 430 431decodes_to(SrcName,DstName) :- 432 translate(SrcName,Name), 433 decodes_to(Name,DstName). 434 435:- export resolve/2. 436resolve(SrcName,DstName) :- 437 name{} = SrcName, 438 name{} = DstName, 439 decodes_to(SrcName,DstName), 440 accept(DstName). 441 442% TODO: Resolve on regions should work like route. 443resolve(SrcRegion,DstRegion) :- 444 to_name(SrcRegion,SrcName), 445 to_name(DstRegion,DstName), 446 resolve(SrcName,DstName), 447 to_region(SrcName,SrcRegion), 448 to_region(DstName,DstRegion). 449 450test_resolve :- 451 %Setup 452 assert(node_translate_dyn( 453 ["In"], [memory, [block{base:1000,limit:2000}]], 454 ["Out1"], [memory, [block{base:0,limit:1000}]])), 455 assert(node_overlay(["In"], ["Out2"])), 456 assert(node_accept(["Out1"], [memory,[block{base:0, limit:2000}]])), 457 assert(node_accept(["Out2"], [memory,[block{base:0, limit:2000}]])), 458 % Hit the translate block 459 resolve( 460 name{node_id:["In"], address:[memory, [1000]]}, 461 name{node_id:["Out1"], address:[memory, [0]]}), 462 % Hit the overlay 463 resolve( 464 name{node_id:["In"], address:[memory, [500]]}, 465 name{node_id:["Out2"], address:[memory, [500]]}). 466 467test_resolve2(Out) :- 468 %Setup 469 assert(node_translate_dyn( 470 ["In1"], [memory, [block{base:1000,limit:2000}]], 471 ["Out1"], [memory, [block{base:0,limit:1000}]])), 472 assert(node_translate_dyn( 473 ["In2"], [memory, [block{base:6000,limit:7000}]], 474 ["Out1"], [memory, [block{base:0,limit:1000}]])), 475 assert(node_accept(["Out1"], [memory,[block{base:0, limit:2000}]])), 476 % Reverse lookup 477 resolve( 478 name{node_id:["In1"], address:[memory, [1000]]}, 479 R), 480 resolve( 481 name{node_id:["In2"], address:Out}, 482 R). 483 484test_resolve3(Out) :- 485 %Setup 486 assert(node_translate_dyn( 487 ["In1"], [memory, [block{base:1000,limit:2000}]], 488 ["Out1"], [memory, [block{base:0,limit:1000}]])), 489 assert(node_translate_dyn( 490 ["In2"], [memory, [block{base:6000,limit:7000}]], 491 ["Out1"], [memory, [block{base:0,limit:1000}]])), 492 assert(node_accept(["Out1"], [memory,[block{base:0, limit:2000}]])), 493 InRegion = region{node_id:["In1"], blocks:[memory, [block{base:1000, limit:1500}]]}, 494 resolve(InRegion,Out). 495 496 497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 498%%%% Load sockeye compiled decoding nets and instantiate modules 499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 500:- export load_net/1. 501load_net(File) :- 502 ensure_loaded(File). 503 504:- export load_module/2. 505load_module(Mod, Id) :- 506 call(Mod, Id). 507 508:- export load_net_module/2. 509load_net_module(File, Mod) :- 510 ensure_loaded(File), 511 call(Mod, []). 512 513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 514%%%% Node enumeration. 515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 516 517:- export alloc_node_enum/1. 518:- dynamic enum_node_id/2. 519:- export enum_node_id/2. 520alloc_node_enum(N) :- alloc_one(node_enum, N). 521 522get_or_alloc_node_enum(NodeId, Enum) :- 523 enum_node_id(Enum, NodeId) ; 524 ( alloc_node_enum(Enum), assert(enum_node_id(Enum, NodeId)) ). 525 526 527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 528%%%% VNode Allocator. 529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 530 531:- dynamic vnode_region/1. 532 533vnode_meta(PageSize, PoolSize) :- 534 PageSize is 2 ^ 12, % 4Kb Pages 535 PoolSize is 2048. % Number of pages 536 537% TODO: Test me 538vnode_alloc(BaseAddr) :- 539 vnode_region(Reg), 540 region_base_name(Reg, RegName), 541 alloc_one(vnodes, Slot), 542 vnode_meta(PageSize,_), 543 RegName = name{address: Addr}, 544 Offset is PagesSize * Slot, 545 address_add_const(Addr, Offset, NewAddr), 546 NewAddr = [memory, [BaseAddr]]. 547 548 549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 550%%%% X86 Support. Complements the sockeye file, should really be moved into its own file 551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 552 553:- export init/0. 554:- export add_pci_alloc/1. 555:- export add_pci/1. 556:- export add_process/1. 557:- export add_process_alloc/1. 558:- export dram_nodeid/1. 559:- export alloc_root_vnodeslot/2. 560:- export free_root_vnodeslot/2. 561 562:- dynamic pci_address_node_id/2. 563:- export pci_address_node_id/2. 564:- dynamic process_node_id/2. 565:- export process_node_id/2. 566 567alloc_root_vnodeslot(NodeId, Slot) :- 568 alloc_one(root_vnodeslot(NodeId), Tmp), 569 Slot is Tmp + 2. 570 571free_root_vnodeslot(NodeId, Slot) :- 572 Tmp is Slot - 2, 573 free_one(root_vnodeslot(NodeId), Tmp). 574 575dram_nodeid(NodeId) :- NodeId = ["DRAM"]. 576 577% This uses the memory_region facts (defined in the main module) to 578% find a region above 4G that we will manage. 579initial_dram_block(Block) :- %a 580 % Find the usable DRAM using the existing SKB facts 581 call(mem_region_type, RamType, ram)@eclipse, 582 findall((Base, Size), call(memory_region,Base,Bits,Size,RamType,Data)@eclipse, MemCandidates), 583 (foreach((Base,Size), MemCandidates), fromto([], In, Out, FiltCandidates) do 584 (((MinBase = 4294967296, % 4G 585 MinSize = 1073741824, % 1G 586 Base >= MinBase, 587 Size >= MinSize) -> Out = [(Base,Size) | In] 588 ) ; ( 589 Out = In 590 )) 591 ), 592 FiltCandidates = [(Base,Size) | _], 593 Limit is Base + Size, 594 Block = block{base:Base, limit: Limit}. 595 596 597init :- 598 add_SYSTEM([]), 599 DRAM_ID = ["DRAM"], 600 initial_dram_block(Block), 601 assert(node_accept(["DRAM"], [memory, [Block]])), 602 get_or_alloc_node_enum(DRAM_ID, DRAM_ENUM), 603 printf("Decoding net initialized using %p as DRAM. DRAM nodeid: %p\n", 604 [Block, DRAM_ENUM]), 605 606 % Manage space for vnodes 607 vnode_meta(PageSize, PoolSize), 608 VnodePoolSize is PageSize * PoolSize, 609 Size = [VnodePoolSize], 610 alloc_range(DRAM_ID, [memory, Size], BaseOut), 611 mark_range_in_use(DRAM_ID, BaseOut, Size), 612 node_in_use(DRAM_ID, Region), 613 assert(vnode_region(Region)), 614 writeln("Using for PageTables:"), writeln(Region). 615 616add_pci :- 617 add_pci(["PCI0"]). 618 619iommu_enabled :- 620 call(iommu_enabled,0,_)@eclipse. 621 622add_pci(Id, Addr) :- 623 PCIBUS_ID = ["PCIBUS"], 624 PCIIN_ID = ["IN" | Id], 625 PCIOUT_ID = ["OUT" | Id], 626 (iommu_enabled -> ( 627 add_PCI_IOMMU(Id), 628 % Mark IOMMU block remappable 629 assert(node_block_meta(["IN", "IOMMU0" | Id], 21, ["OUT", "IOMMU0" | Id])), 630 % And assign a root PT 631 pt_alloc(Root), 632 assert(node_pt(["IN", "IOMMU0", Id], Root, ["OUT","IOMMU0",Id])) 633 ) ; ( 634 % IOMMU disabled. 635 add_PCI(Id) 636 )), 637 % connect the output to the systems pci bus 638 assert(node_overlay(PCIOUT_ID, PCIBUS_ID)), 639 % Now insert the BAR into the PCI bus address space 640 findall((Addr, BarNum, BarStart, BarSize), 641 call(bar(Addr, BarNum, BarStart, BarSize, mem, _, _))@eclipse, 642 Bars), 643 (foreach((_, BarNum, BarStart, BarSize), Bars), param(Id), param(PCIBUS_ID) do 644 BarId = [BarNum, "BAR" | Id], 645 BarEnd is BarStart + BarSize, 646 assert(node_accept(BarId, [memory,[block{base:BarStart,limit:BarEnd}]])), 647 assert(node_translate_dyn(PCIBUS_ID, [memory,[block{base:BarStart,limit:BarEnd}]], BarId, 648 [memory, [block{base:BarStart,limit:BarEnd}]])) 649 ). 650 651 652add_pci_alloc(Addr) :- 653 alloc_node_enum(Enum), 654 add_pci([Enum], Addr), 655 % Set it to the node id where addresses are issued from the PCI device 656 OutNodeId = ["OUT", "PCI0", Enum], 657 assert(enum_node_id(Enum, OutNodeId)), 658 assert(pci_address_node_id(Addr, Enum)). 659 660add_process_alloc(Enum) :- 661 alloc_node_enum(Enum), 662 add_process([Enum]), 663 % Set it to the node id where addresses are issued from the process 664 assert(enum_node_id(Enum, ["OUT", "PROC0", Enum])). 665 %assert(process_node_id(ProcId, Enum)). 666 667 668% Make ID argument if we want to add multiple. 669add_process :- 670 add_process(["PROC0"]). 671 672add_process(Id) :- 673 DRAM_ID = ["DRAM"], 674 add_PROC_MMU(Id), 675 676 % Mark MMU block remappable 677 MMU_IN_ID = ["IN", "MMU0" | Id], 678 MMU_OUT_ID = ["OUT", "MMU0" | Id], 679 assert(node_block_meta(MMU_IN_ID, 21, MMU_OUT_ID)), % Make MMU configurable 680 pt_alloc(Root), 681 assert(node_pt(MMU_IN_ID, Root, MMU_OUT_ID)), 682 683 OUT_ID = ["OUT" | Id], 684 assert(node_overlay(OUT_ID, DRAM_ID)), 685 % Reserve memory for the process, the OUT/PROC0 node is the one where 686 % initially the process (virtual) addresses are issued. 687 Limit = 1099511627775, % (512 << 31) - 1 688 assert(node_in_use(["OUT", "PROC0" | Id], [memory, [block{base:0, limit: Limit}]])). 689 690 691 692%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 693%%%% Mark ranges used and Query them 694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 695 696:- export free_region/3. 697% Puts IC constraints on the variables 698free_region(NodeId, _, Out) :- 699 % Not a very smart allocator, finds the highest addr in use and append 700 % Therefore can ignore Size 701 findall(X, node_in_use(NodeId, X), UsedBlockLi), 702 block_address_gt([memory, [block{limit: -1}]], Out), % TODO: Works only for 1 Dim addr. 703 (foreach(UsedBlock, UsedBlockLi), param(Out) do 704 block_address_gt(UsedBlock, Out) 705 ). 706 707:- export free_region/2. 708% Puts IC constraints on the variables 709free_region(Name, Size) :- 710 name{ 711 node_id: NodeId, 712 address: Out 713 } = Name, 714 free_region(NodeId, Size, Out). 715 716free_region(Region, Size) :- 717 region_base_name(Region, Name), 718 free_region(Name, Size). 719 720% Resolves the variables 721free_region_aligned(Region, Size) :- 722 is_list(Size), 723 region_base_name(Region, BaseName), 724 free_region(BaseName, Size), 725 name_aligned(BaseName, 21), 726 term_variables(BaseName, BaseNameVars), 727 labeling(BaseNameVars), 728 region_size(Region, Size). 729 730% Resolves the variables 731free_accepted_region_aligned(Region, Size) :- 732 is_list(Size), 733 region_base_name(Region, BaseName), 734 free_region(BaseName, Size), 735 name_aligned(BaseName, 21), 736 accept(BaseName), 737 term_variables(BaseName, BaseNameVars), 738 labeling(BaseNameVars), 739 region_size(Region, Size). 740 741%:- export free_region/1. 742%free_region(Region) :- 743% region_size(Region, Size), % Determine size using the base/limit in the region. 744% free_region(Region, Size). 745 746 747%% NodeId:: Addr, Size :: Addr, Out :: Addr 748:- export alloc_range/3. 749alloc_range(NodeId, Size, Out) :- 750 free_region(NodeId, Size, Out), 751 term_variables(Out, OutVars), 752 labeling(OutVars). 753 754 755% After finding a range with alloc range, you actually want to mark it used 756% with this function. 757mark_range_in_use(NodeId, Addr, ISize) :- 758 Addr = [Kind, IAddr], 759 (foreach(UsedBlock, UsedBlockLi), foreach(A, IAddr), foreach(S,ISize) do 760 Limit is A + S, 761 UsedBlock = block{ 762 base: A, 763 limit: Limit 764 } 765 ), 766 assert(node_in_use(NodeId, [Kind, UsedBlockLi])). 767 768mark_range_in_use(Name, ISize) :- 769 name{ 770 node_id: NodeId, 771 address: Addr 772 } = Name, 773 mark_range_in_use(NodeId, Addr, ISize). 774 775mark_range_in_use(Region) :- 776 Region = region{ node_id: NodeId, blocks: Blocks }, 777 assert(node_in_use(NodeId, Blocks)). 778 779 780:- export mark_range_free/2. 781mark_range_free(NodeId, Base) :- 782 retract(node_in_use(NodeId, [memory, [block{base: Base}]])). 783 784:- export test_alloc_range/0. 785test_alloc_range :- 786 Id = [], 787 % Test setup 788 mark_range_in_use(Id, [memory, [0]], [1000]), 789 790 % First allocation 791 Size = [1000], 792 alloc_range(Id, [memory, Size], Out), 793 mark_range_in_use(Id, Out, Size), 794 writeln("First allocation: "), 795 writeln(Out), 796 797 % Second allocation 798 Size2 = [5000], 799 alloc_range(Id, [memory, Size2], Out2), 800 mark_range_in_use(Id, Out2, Size2), 801 writeln("Second allocation: "), 802 writeln(Out2). 803 804% Find a unused buffer, using already set up routing. 805% Node1 :: Addr, Node2 :: Name, Resolved :: Name 806common_free_buffer_existing(BufferSize, Node1, Node2, Resolved) :- 807 free_region(Node1, [memory, [BufferSize]]), 808 free_region(Node2, [memory, [BufferSize]]), 809 resolve(Node1, Resolved), 810 resolve(Node2, Resolved), 811 free_region(Resolved, BufferSize), 812 term_variables(Resolved, Vars), 813 labeling(Vars). 814 815test_common_free_buffer_existing(Proc,Pci,Resolved) :- 816 init, add_pci, add_process, 817 BUFFER_SIZE = 1024, 818 Proc = name{node_id: ["OUT", "PROC0", "PROC0"]}, 819 Pci = name{node_id: ["OUT", "PCI0", "PCI0"]}, 820 common_free_buffer_existing(BUFFER_SIZE, Proc, Pci, Resolved). 821 822% Like common_free_buffer_existing, but allow reconfiguration of nodes (routing) 823% Find two regions N1Region and N2Region, that resolve to a free region. 824:- export common_free_buffer/5. 825common_free_buffer(Size, N1Region, N2Region, ResRegion, Route) :- 826 is_list(Size), 827 828 N1Region = region{blocks: [memory, [_]]}, 829 N2Region = region{blocks: [memory, [_]]}, 830 ResRegion = region{blocks: [memory, [block{base:Base, limit: Limit}]]}, 831 832 % nail down the regions 833 free_region_aligned(N1Region, Size), 834 free_region_aligned(N2Region, Size), 835 free_accepted_region_aligned(ResRegion, Size), 836 accept(ResRegion), 837 838 route(N1Region, ResRegion, R1), 839 route(N2Region, ResRegion, R2), 840 841 union(R1,R2,Route). 842 843% Find two regions N1Region and N2Region, that resolve to an existing result region. 844:- export common_free_map/5. 845common_free_map(Size, N1Region, N2Region, ResRegion,Route) :- 846 is_list(Size), 847 848 N1Region = region{blocks: [memory, [_]]}, 849 N2Region = region{blocks: [memory, [_]]}, 850 ResRegion = region{blocks: [memory, [block{base:Base, limit: Limit}]]}, 851 852 % nail down the input regions first 853 free_region_aligned(N1Region, Size), 854 free_region_aligned(N2Region, Size), 855 856 route(N1Region, ResRegion, R1), 857 route(N2Region, ResRegion, R2), 858 859 labeling([Base,Limit]), 860 union(R1,R2,Route). 861 862% the function called from mem_serv 863:- export alloc_common/4. 864% Allocate a Buffer with Bits size, reachable from N1 and N2. Mark the resolved 865% region as in use. 866alloc_common(Bits, N1Enum, N2Enum, DestEnum) :- 867 enum_node_id(N1Enum, N1Id), 868 enum_node_id(N2Enum, N2Id), 869 enum_node_id(DestEnum, DestId), 870 R1 = region{node_id: N1Id, blocks: [memory, [block{base:R1Addr}]]}, 871 R2 = region{node_id: N2Id, blocks: [memory, [block{base:R2Addr}]]}, 872 Dest = region{node_id: DestId, blocks: [memory, [block{base:DestAddr}]]}, 873 Size is 2 ^ Bits - 1, 874 common_free_buffer([memory, [Size]], R1, R2, Dest, _), 875 mark_range_in_use(Dest), 876 writeln([name(R1Addr, N1Enum),name(R2Addr, N2Enum),name(DestAddr, DestEnum)]). 877 878% Like alloc_common/4, but tries to determine destination node automatically. 879:- export alloc_common/3. 880alloc_common(Bits, N1Enum, N2Enum) :- 881 DRAM = ["DRAM"], 882 get_or_alloc_node_enum(DRAM, DramEnum), 883 alloc_common(Bits, N1Enum, N2Enum, DramEnum). 884 885% Find names in N1 and N2 that resolve to ResAddr, then mark those used. 886:- export map_common/4. 887map_common(Bits, ResRAddr, N1Enum, N2Enum) :- 888 enum_node_id(N1Enum, N1Id), 889 enum_node_id(N2Enum, N2Id), 890 R1 = region{node_id: N1Id, blocks: [memory, [block{base:R1Addr}]]}, 891 R2 = region{node_id: N2Id, blocks: [memory, [block{base:R2Addr}]]}, 892 Size is 2 ^ Bits - 1, 893 ResR = region{blocks: [memory, [block{base:ResRAddr}]]}, 894 common_free_map([memory, [Size]], R1, R2, ResR, _), 895 ResR = region{node_id: ResRId}, 896 get_or_alloc_node_enum(ResRId, ResEnum), 897 mark_range_in_use(R1), 898 mark_range_in_use(R2), 899 writeln([name(R1Addr, N1Enum),name(R2Addr, N2Enum),name(ResRAddr, ResEnum)]). 900 901 902% Translate a name from one Node to a name to another node, so they 903% resolve to the same ressource. 904:- export change_view/2. 905change_view(Name1, Name2) :- 906 resolve(Name1, D), 907 resolve(Name2, D). 908 909test_change_view(pci) :- 910 init, add_pci, add_process, 911 Proc = name{node_id: ["OUT", "PROC0", "PROC0"], address: [memory, [0]]}, 912 Pci = name{node_id: ["OUT", "PCI0", "PCI0"]}, 913 change_view(Proc, Pci). 914 915 916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 917%%%% Bit Array Representation 918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 919 920array_all_eq([], _). 921array_all_eq([A | Bs], A) :- array_all_eq(Bs,A). 922array_all_eq(Arr, A) :- array_list(Arr, ArrLi), array_all_eq(ArrLi, A). 923 924% Constrains word to be a N bit word 925assert_word(W, N) :- 926 dim(W,[N]), W :: [0 .. 1]. 927 928% This beauty converts words (array of bit values) to a numeric representation. 929word_to_num(W, Num) :- 930 dim(W, [Len]), 931 (for(I,1,Len), fromto(0,In,Out,NumT), param(W) do 932 Out = W[I] * 2^(I-1) + In), 933 Num $= eval(NumT). 934 935% A part of word is etracted into Subword, Range specifies 936subword(Word,Subword, Range) :- 937 SW is Word[Range], 938 array_list(Subword,SW). 939 940 941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 942%%%% Block Remappable Nodes 943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 944 945% As of now, block remappable implies that the node is one dimension 946:- dynamic node_block_conf/3. %(NodeId, VPN, PPN). 947:- dynamic node_block_meta/3. %(NodeId, BlockSizeBits, OutNodeId) 948 949% Translate using Block Conf. Same signature as node_translate 950node_translate_block(InNodeId, [memory, [VAddr]], OutNodeId, [memory, [PAddr]]) :- 951 node_block_meta(InNodeId, BlockSizeBits, OutNodeId), 952 % Bit-Lookup Offset and VPN 953 assert_word(VAW, 48), 954 word_to_num(VAW, VAddr), 955 subword(VAW, VAOffsetW, 1 .. BlockSizeBits), 956 VPNStartBit is BlockSizeBits + 1, 957 subword(VAW, VPNW, VPNStartBit .. 48), 958 word_to_num(VPNW, VPN), 959 960 % Lookup PPN and PA offset 961 node_block_conf(InNodeId, VPN, PPN), 962 963 % Stich together PA 964 assert_word(PAW, 48), % TODO bit size for physical address? 965 subword(PAW, VAOffsetW, 1 .. 21), 966 subword(PAW, PPNW, 22 .. 48), 967 word_to_num(PPNW, PPN), 968 969 word_to_num(PAW, PAddr). 970 971test_node_translate_block :- 972 assert(node_block_conf([], 0, 1)), 973 assert(node_block_meta([], 21, ["OUT"])), 974 node_translate_block([], [memory,[1000]], ["OUT"], [memory, [2098152]]). 975 976 977%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 978%%%% Routing 979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 980 981one_block_upper_limit(Name, Limit) :- 982 Name = name{ 983 node_id: NodeId, 984 address: Address 985 }, 986 (( % Block remappable? 987 node_block_meta(NodeId, BlockSizeBits, _), 988 Address = [K, [A]], 989 Limit = [K, [ILimit]], 990 assert_word(AW, 48), 991 word_to_num(AW, A), 992 assert_word(LimitW, 48), % TODO bit size for physical address? 993 UpperStartBit is BlockSizeBits + 1, 994 subword(AW, UpperW, UpperStartBit .. 48), 995 subword(LimitW, UpperW, UpperStartBit .. 48), 996 subword(LimitW, LowerW, 1 .. BlockSizeBits), 997 array_all_eq(LowerW, 1), 998 word_to_num(LimitW, ILimit) 999 ) ; ( 1000 % In a translate block? 1001 node_translate_dyn(NodeId, Block, _, _), 1002 address_match(Address, Block), 1003 block_limit_address(Block, Limit) 1004 ) ; ( 1005 node_overlay(NodeId, _), 1006 Limit = [memory, [281474976710656]] % Default limit 1007 )). 1008 1009test_one_block_upper_limit :- 1010 assert(node_translate_dyn( 1011 ["In"], [memory, [block{base:1000,limit:2000}]], 1012 ["Out1"], [memory, [block{base:0,limit:1000}]])), 1013 one_block_upper_limit(name{node_id:["In"],address:[memory, [1400]]}, Limit), 1014 writeln(Limit), 1015 1016 assert(node_block_meta(["In1"], 21, ["Out1"])), 1017 one_block_upper_limit(name{node_id:["In1"],address:[memory, [1400]]}, Limit2), 1018 writeln(Limit2). 1019 1020% Internal function for region route 1021 1022% Route Step for names 1023route_step(SrcName, NextName, Route) :- 1024 SrcName = name{}, 1025 NextName = name{}, 1026 (( 1027 translate(SrcName, NextName), 1028 Route = [] 1029 ) ; ( 1030 % In this case, we can assume, there is no block map existing, 1031 % But, we have to check if the node supports block mapping, then 1032 % we can install this 1033 can_translate(SrcName, NextName, Config), 1034 Route = [Config] 1035 )). 1036 1037% Route Step for regions 1038route_step(SrcRegion, NextRegion, Route) :- 1039 % This will only work, if the source region will translate to the same node 1040 % block this should be ensured by the block splitting of the region route 1041 region_base_name(SrcRegion, SrcBase), 1042 region_limit_name(SrcRegion, SrcLimit), 1043 route_step(SrcBase, NextBase, Route1), 1044 route_step(SrcLimit, NextLimit, Route2), 1045 region_base_name(NextRegion, NextBase), 1046 region_limit_name(NextRegion, NextLimit), 1047 union(Route1, Route2, Route). 1048 1049% Routing functionality for ranges addresses (represented as region) 1050% This one 1051route(SrcRegion, DstRegion, Route) :- 1052 SrcRegion = region{node_id: SrcNodeId, blocks: SrcBlocks}, 1053 DstRegion = region{node_id: DstNodeId, blocks: DstBlocks}, 1054 block_base_address(SrcBlocks, SrcBase), % SrcBase = [memory, [0,1,2]] 1055 block_limit_address(SrcBlocks, SrcLimit), % SrcLimit = [memory, [10,11,12]] 1056 block_base_address(DstBlocks, DstBase), 1057 block_limit_address(DstBlocks, DstLimit), 1058 one_block_upper_limit(name{node_id:SrcNodeId, address:SrcBase}, BlockLimit), 1059 % BlockLimit = [memory, [2000]] 1060 (( 1061 address_gte(SrcLimit , BlockLimit), 1062 % Great, SrcRegion fits completly in translate block 1063 route_step(SrcRegion, NextRegion, R1), 1064 ( accept(NextRegion) -> ( 1065 DstRegion = NextRegion, 1066 Route = R1 1067 ) ; ( 1068 route(NextRegion, DstRegion, R2), 1069 union(R1, R2, Route) 1070 )) 1071 ) ; ( 1072 % Only allow this if the block has to be split 1073 not(address_gte(SrcLimit, BlockLimit)), 1074 1075 % Route first block 1076 block_base_address(NewSrcBlocks, SrcBase), % Keep the base 1077 block_limit_address(NewSrcBlocks, BlockLimit), 1078 1079 block_base_address(NewDstBlocks, DstBase), % Keep the base 1080 address_sub(BlockLimit, SrcBase, BlockSize), 1081 address_add(DstBase, BlockSize, NewDstLimit), 1082 block_limit_address(NewDstBlocks, NewDstLimit), 1083 route( 1084 region{node_id:SrcNodeId, blocks: NewSrcBlocks}, 1085 region{node_id:DstNodeId, blocks: NewDstBlocks}, 1086 R1), 1087 1088 % Construct remainder 1089 address_add_const(BlockLimit, 1, AfterBlock), 1090 block_base_address(RemSrcBlocks, AfterBlock), 1091 block_limit_address(RemSrcBlocks, SrcLimit), % keep limit 1092 1093 address_add_const(NewDstLimit, 1, AfterDstLimit), 1094 block_base_address(RemDstBlocks, AfterDstLimit), 1095 block_limit_address(RemDstBlocks, DstLimit), % keep limit 1096 route( 1097 region{node_id:SrcNodeId, blocks: RemSrcBlocks}, 1098 region{node_id:DstNodeId, blocks: RemDstBlocks}, 1099 R2), 1100 1101 % Concat routes 1102 union(R1, R2, Route) 1103 )). 1104 1105 1106% Routing functionality for single addresses (represented as name) 1107route(SrcName, DstName, Route) :- 1108 SrcName = name{}, 1109 DstName = name{}, 1110 route_step(SrcName, NextName, R1), 1111 ( accept(NextName) -> ( 1112 DstName = NextName, 1113 Route = R1 1114 ) ; ( 1115 route(NextName, DstName, R2), 1116 union(R1, R2, Route) 1117 )). 1118 1119route_and_install(SrcName, DstName, Route) :- 1120 route(SrcName,DstName, Route), 1121 term_variables(Route, RouteVars), 1122 labeling(RouteVars), 1123 install_route(Route). 1124 1125 1126test_route(Route) :- 1127 init, add_pci, add_process, 1128 MMU_IN_ID = ["IN", "MMU0", "PROC0"], 1129 MMU_OUT_ID = ["OUT", "MMU0", "PROC0"], 1130 DRAM_ID = ["DRAM"], 1131 retract(node_translate_dyn(MMU_IN_ID,_,_,_)), % Make sure there is no route 1132 assert(node_block_meta(MMU_IN_ID, 21, MMU_OUT_ID)), 1133 alloc_range(DRAM_ID, [memory, [1024]], FreeDRAM), 1134 SrcName=name{node_id: MMU_IN_ID, address: [memory, [0]]}, 1135 DstName=name{node_id: DRAM_ID, address: FreeDRAM}, 1136 writeln(("Routing from", SrcName, " to ", DstName)), 1137 route(SrcName, DstName, Route), 1138 term_variables(Route, RouteVars), 1139 labeling(RouteVars), 1140 writeln(("Calculated", Route)). 1141 1142test_route2 :- 1143 init, add_pci, add_process, 1144 MMU_IN_ID = ["IN", "MMU0", "PROC0"], 1145 MMU_OUT_ID = ["OUT", "MMU0", "PROC0"], 1146 DRAM_ID = ["DRAM"], 1147 retract(node_translate_dyn(MMU_IN_ID,_,_,_)), % Make sure there is no route 1148 assert(node_block_meta(MMU_IN_ID, 21, MMU_OUT_ID)), 1149 alloc_range(DRAM_ID, [memory, [1024]], FreeDRAM), 1150 SrcName1=name{node_id: MMU_IN_ID, address: [memory, [0]]}, 1151 DstName1=name{node_id: DRAM_ID, address: FreeDRAM}, 1152 writeln(("Routing from", SrcName1, " to ", DstName1)), 1153 route_and_install(SrcName1, DstName1, Route1), 1154 writeln(("Calculated (1)", Route1)), 1155 SrcName2=name{node_id: MMU_IN_ID, address: [memory, [0]]}, 1156 DstName2=name{node_id: DRAM_ID, address: FreeDRAM}, 1157 writeln(("Routing from", SrcName2, " to ", DstName2)), 1158 route_and_install(SrcName2, DstName2, Route2), 1159 writeln(("Calculated (2) ", Route2)). 1160 1161 1162test_route_region_small :- 1163 assert(node_block_meta(["IN"], 21, ["OUT"])), 1164 assert(node_accept(["OUT"], [memory, [block{base:0, limit:100000}]])), 1165 route( 1166 region{node_id:["IN"], blocks:[memory,[block{base:0, limit:1000}]]}, 1167 region{node_id:["OUT"], blocks:[memory,[block{base: 0, limit: 1000}]]}, 1168 Route), 1169 term_variables(Route, RouteVars), 1170 labeling(RouteVars), 1171 1172 % check if it worked 1173 member((["IN"], 0, 0), Route), 1174 writeln(("Calculated", Route)). 1175 1176test_route_region_two :- 1177 Limit = 4194302, % 0x1fffff * 2 1178 assert(node_block_meta(["IN"], 21, ["OUT"])), 1179 assert(node_accept(["OUT"], [memory, [block{base:0, limit:4194303}]])), 1180 route( 1181 region{node_id:["IN"], blocks:[memory,[block{base:0, limit:Limit}]]}, 1182 region{node_id:["OUT"], blocks:[memory,[block{base: 0, limit: Limit}]]}, 1183 Route), 1184 term_variables(Route, RouteVars), 1185 labeling(RouteVars), 1186 1187 member((["IN"], 0, 0), Route), 1188 member((["IN"], 1, 1), Route), 1189 writeln(("Calculated", Route)). 1190 1191 1192test_route_all :- 1193 test_route_region_small, 1194 writeln("region small: passed"), 1195 test_route_region_two, 1196 writeln("region two: passed"). 1197 1198 1199% It is possible, to remap SrcName to DstName using the block remapping? 1200can_translate(SrcName, DstName, (SrcNodeId, (VPN, PPN))) :- 1201 name{ node_id: SrcNodeId, address: [K, [ISrcAddr]] } = SrcName, 1202 name{ node_id: DstNodeId, address: [K, [IDstAddr]] } = DstName, 1203 node_block_meta(SrcNodeId, BlockSizeBits, DstNodeId), 1204 split_vaddr(ISrcAddr, BlockSizeBits, [VPN, Offset]), 1205 split_vaddr(IDstAddr, BlockSizeBits, [PPN, Offset]), 1206 not(node_block_conf(SrcNodeId, VPN, _)). % No mapping with this VPN 1207 1208:- export install_route/1. 1209install_route([]). 1210 1211install_route([null | Ri]) :- 1212 install_route(Ri). 1213 1214install_route([(NodeId, VPN, PPN) | Ri]) :- 1215 assert(node_block_conf(NodeId, VPN, PPN)), 1216 install_route(Ri). 1217 1218 1219 1220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1221%%%% X86 Page table configurable nodes 1222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1223 1224% node_pt stores the root page table for per node 1225:- dynamic node_pt/3. % NodeId, RootPt, OutNodeId 1226:- dynamic pt/3. % PtIndex, Offset, NextPtIndex 1227:- export pt/3. 1228 1229split_vpn(VPN, Parts) :- 1230 assert_word(VPNW, 27), % 3 x 9 = 27 1231 word_to_num(VPNW, VPN), 1232 subword(VPNW, L1W, 1 .. 9), 1233 word_to_num(L1W, L1), 1234 subword(VPNW, L2W, 10 .. 18), 1235 word_to_num(L2W, L2), 1236 subword(VPNW, L3W, 19 .. 27), 1237 word_to_num(L3W, L3), 1238 Parts = [L3,L2,L1]. 1239 1240split_vaddr(VA, BlockSizeBits, [VPN, Offset]) :- 1241 assert_word(VAW, 48), 1242 word_to_num(VAW, VA), 1243 subword(VAW, OffsetW, 1 .. BlockSizeBits), 1244 word_to_num(OffsetW, Offset), 1245 VPNWStart is BlockSizeBits + 1, 1246 subword(VAW, VPNW, VPNWStart .. 48), 1247 word_to_num(VPNW, VPN). 1248 1249test_split_vpn :- 1250 VA = 16'40201, % hex(1 | 1<<9 | 1<<18) 1251 split_vpn(VA, [1,1,1]). 1252 1253pt_alloc(Idx, Idx) :- 1254 not(pt(Idx,_,_)). 1255 1256pt_alloc(Try, Idx) :- 1257 NextTry is Try + 1, 1258 pt_alloc(NextTry, Idx). 1259 1260:- export pt_alloc/1. 1261pt_alloc(Idx) :- 1262 pt_alloc(0,Idx), !, 1263 assert(pt(Idx,-1,-1)). 1264 1265% Inner function for pt_delta, better not use anywhere else. 1266pt_delta_append_if_new(In, Out, pt(A,B,C)) :- 1267 nonvar(A), nonvar(B), 1268 % C magic: nonvar, or already installed, or it will be new allocated 1269 (nonvar(C) ; (pt(A,B,C) ; pt_alloc(C))), 1270 (pt(A,B,C) -> In = Out ; (Out = [pt(A,B,C) | In])). 1271 1272% Get the difference between the PT facts (which reflect in memory contents) 1273% and the newly to be inserted block translate facts. 1274% BlockDelta :: [(VPN, PPN), ...] 1275% PtDelta :: [pt(Idx, Offset, NextIdx), ...] 1276:- export pt_delta/3. 1277pt_delta(NodeId, BlockDelta, PtDelta) :- 1278 %findall((VPN, PPN), node_block_conf(NodeId, VPN, PPN), Maps), 1279 (foreach((VPN,PPN), BlockDelta),param(NodeId),fromto([], In, Out, PtDelta) do 1280 node_pt(NodeId, RootPt, _), 1281 split_vpn(VPN, [L3,L2,L1]), 1282 pt_delta_append_if_new(In,Out1, pt(RootPt, L3, L2Idx)), 1283 pt_delta_append_if_new(Out1,Out2, pt(L2Idx, L2, L1Idx)), 1284 pt_delta_append_if_new(Out2,Out, pt(L1Idx, L1, PPN)) 1285 ). 1286 1287:- export test_pt_delta/1. 1288test_pt_delta(PtDelta) :- 1289 pt_alloc(Root), 1290 assert(node_pt([],Root,["OUT"])), 1291 pt_delta([], [(0,23)], PtDelta). 1292 % PtDelta = [pt(2, 0, 23), pt(1, 0, 2), pt(0, 0, 1)] modulo reordering. 1293 1294% For a list of PTs, assert the facts. 1295:- export assert_pt_delta/1. 1296assert_pt_delta(PtDelta) :- 1297 (foreach(X, PtDelta) do 1298 assert(X) 1299 ). 1300 1301:- export route_node_ids/2. 1302route_node_ids([], []). 1303route_node_ids([(NodeId,_) | As], Res) :- 1304 route_node_ids(As,Res1), 1305 union([NodeId], Res1, Res). 1306 1307:- export route_conf_for_id/3. 1308route_conf_for_id([], _, []). 1309route_conf_for_id([(NodeId,Conf) | As], NodeId, Res) :- 1310 route_conf_for_id(As, NodeId, Res1), 1311 union([Conf],Res1, Res). 1312 1313route_conf_for_id([_ | As], NodeId, Res) :- 1314 route_conf_for_id(As, NodeId, Res). 1315 1316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1317%%%% Big tests 1318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1319:- export test_all/0. 1320test_all :- 1321 init, add_pci, add_process, 1322 BUFFER_SIZE = [memory, [1024]], 1323 ProcId = ["OUT", "PROC0", "PROC0"], 1324 Proc = region{node_id: ProcId}, 1325 PciId = ["OUT", "PCI0", "PCI0"], 1326 Pci = region{node_id: PciId}, 1327 common_free_buffer(BUFFER_SIZE, Proc, Pci, Resolved, Route), 1328 writeln(("Free buffer reachable from Proc and Pci found", Resolved)), 1329 writeln(("View from PCI ", Pci)), 1330 writeln(("View from Proc ", Proc)), 1331 writeln(("Using Block-Configuration ", Route)), 1332 route_node_ids(Route, NodeIds), 1333 (foreach(NodeId, NodeIds),param(Route) do 1334 route_conf_for_id(Route, NodeId, BlockDelta), 1335 pt_delta(NodeId, BlockDelta, PtDelta), 1336 writeln(("New PT entries for ", NodeId, " -> ", PtDelta)) 1337 ), 1338 writeln("Installing Route"), 1339 install_route(Route), 1340 !, % make sure not to back track past the state modifying install_route 1341 region_base_name(Proc,ProcBaseAddr), % The first byte of the common region. 1342 PciBaseAddr = name{node_id:PciId}, 1343 change_view(ProcBaseAddr, PciBaseAddr), 1344 writeln(("Lookup from Proc to PCI BaseAddr", PciBaseAddr)). 1345 1346:- export test_mem_if/0. 1347test_mem_if :- 1348 add_pci_alloc(addr(0,1,2)), 1349 pci_address_node_id(addr(0,1,2), PciEnum), 1350 add_process_alloc(proc(0)), 1351 process_node_id(proc(0), ProcEnum), 1352 alloc_common(21, PciEnum, ProcEnum). 1353 1354 1355:- export dec_net_debug/0. 1356dec_net_debug :- listing. 1357